[프로그램 개발 이유]
토익 공부를 하다가 단어시험을 보려고 했는데 내가 외운 순서대로만 외우다 보니 순서가 뒤바뀌거나 다른 지문에서 해당 단어가 튀어 나왔을 때 단어의 뜻이 확실하게 떠오르지가 않았다.
그래서 단어를 확실하게 외웠는지 확인할 수 있게 단어를 나 대신 무작위로 섞어주는 프로그램을 만들기로 했다.
excel로도 만들 수 있지만 그래도 웹에서 동작하도록 만들면 밖에서도 편하게 접근하여 이용할 수 있을 것 같아서 Google SpreadSheet의 Google Apps Script를 이용하여 개발하였다.
[프로그램 필수요구사항]
- 단어장에서 단어를 가져올 것
- 단어들이 무작위로 섞일 것
[프로그램 설명]
프로그램의 핵심 알고리즘은 59번째 줄부터 65번째 줄이다.
해당 알고리즘은 단어들의 갯수와 같은 크기의 배열에서 랜덤한 값을 선택한 뒤 그 값을 시험지에 적고 해당 값의 배열의 방을 삭제한다. 그리고 다시 랜덤한 값을 뽑을 때는 해당 배열의 크기 안에서만 도출될 수 있겠끔했다.
즉, 배열의 값이 가리키는 부분의 단어를 단어장에서 시험지로 옮겨적은뒤 해당 배열을 삭제함으로써 중복된 값이 생기는 것을 방지하는 것이 소스코드의 핵심 알고리즘이다.
- count_element_in_col(sheet, start_row, col) {}
이 함수는 sheet객체, start_row(카운팅을 시작할 행), col(카운팅할 열)을 입력받아서 해당 열에 연속된 데이터가 있을 때까지 동작한다. 먄약 데이터를 계속해서 만난다면 cnt를 증가시킴으로써 데이터를 계속 카운팅한다. 하지만 데이터가 끊기면 break로 반복문을 탈출한 뒤 카운팅된 수를 리턴해준다. 궁극적으로 해당 열에 몇 개의 데이터가 존재하는지 리턴해주는 함수이다.
- search_first_from_a_row(srh_row,sheet,value,lcrc) {}
이 함수는 srh_row(값을 찾을 행), sheet객체, value(찾을 값), lcrc('LC' or 'RC')를 매개변수로 받는다. 그 후 srh_row에서 value를 찾아서 그 열을 돌려주되 lcrc의 값이 LC이면 그냥 돌려주고 RC이면 +2를 해서 돌려주는 함수이다. 궁극적으로 테스트가 몇 회차인지에 따라 데이터(단어들)을 입력할 Test 회차의 열 번호를 리턴해주는 함수이다.
- random_test() {}
이 함수는 main함수이다. 단어장 시트와 시험지 시트의 객체를 각각 부여받아서 단어장에서 단어들을 읽을 열과 시험지에 단어들을 쓸 열을 저장한다. 그 후 문제의 갯수를 가져와서 해당 갯수만큼의 크기의 배열을 선언한 뒤 1~n(단어의 갯수)로 초기화한다. 그리고 단어가 쓰여질 부분의 셀들을 깨끗이 지운다음 핵심 알고리즘을 이용하여 단어장에서 단어를 읽어서 시험지에 써주는 역할을 한다.
[소소 코드]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
function count_element_in_col(sheet,start_row,col) {
var data;
var cnt = 0;
for(var i = start_row ; true ; i++) {
data = sheet.getRange(i,col).getValue();
if (data !== null && data !== '') cnt++;
else break;
}
return cnt;
};
function search_first_from_a_row(srh_row,sheet,value,lcrc) {
// column를 증가시키면서 해당 row에 값이 있는지 없는지 판별한다.
// 있다면 첫번째로 만난 column을 반환해준다.
var datas = sheet.getRange(1,1,1,sheet.getLastColumn()).getValues();
var col;
for(var i = 0 ; i < datas[0].length ; i ++) {
if(datas[0][i] === value) {
col = i+1;
break;
}
}
// 시험이 LC인지 RC인지에 따라 열의 수가 달라짐.
if(lcrc=='LC');
else if(lcrc=='RC') col+=2;
return col;
};
function random_test() {
var sheet_test = SpreadsheetApp.getActive().getSheetByName('시험지');
var sheet_voca = SpreadsheetApp.getActive().getSheetByName('단어장');
// 단어장 시트에서 시험지 시트의 A2의 test번호와 B2의 LC/RC에 맞는 열을 저장함.
var read_col = search_first_from_a_row(1,sheet_voca,sheet_test.getRange('A2').getValue(),sheet_test.getRange('B2').getValue());
// 시험지 시트에서 시험지 시트의 A2의 test번호와 B2의 LC/RC에 맞는 열을 저장함.
var write_col = search_first_from_a_row(1,sheet_test,sheet_test.getRange('A2').getValue(),sheet_test.getRange('B2').getValue());
// 단어들이 기입될 시작 행
var start_row = 3;
// 문제의 갯수를 가져온다.
var cnt = count_element_in_col(sheet_voca,3,read_col);
// 해당 갯수만큼 배열을 생성한다.
var nums = [];
// 해당 배열을 1~cnt(문제의 갯수)로 초기화 한다.
for(var i = 0 ; i < cnt ; i++) nums.push(i);
// 난수가 입력될 변수
var indexOfNum;
// 만약 단어장에 단어가 없다면 메시지를 띄워주고 종료한다.
if(cnt==0) {
Browser.msgBox('단어장에 단어들을 입력해주세요.');
return;
}
// 단어들이 입력될 cell을 지운다. getRange(시작 행, 시작 열, [시작행에서 움직일 셀 수, 시작열에서 움직일 셀 수]) []: optional
sheet_test.getRange(start_row, write_col, cnt, 1).clear({contentsOnly: true, skipFilteredRows: true});
for(var i = 3 ; i < cnt+start_row ; i++) {
// 배열의 갯수만큼의 수까지 에서 랜덤한 숫자를 하나 뽑는다. 이 값은 중복될 수 있음.
indexOfNum = Math.floor(Math.random() * nums.length);
// 랜덤으로 뽑힌 숫자에 해당하는 배열의 값의 위치에 있는 단어를 시험지에 순서대로 기입한다.
sheet_test.getRange(i,write_col).setValue(sheet_voca.getRange(nums[indexOfNum]+start_row,read_col).getValue());
// 랜덤으로 뽑힌 숫자에 해당하는 배열의 방을 배열에서 삭제한다.
nums.splice(nums.indexOf(nums[indexOfNum]), 1);
}
};
|
cs |
[동작 시연]
단어장은 위 그림과 같이 생겼다.
시험지는 위 그림과 같이 생겼다.
시험지의 왼쪽 윗부분에 보면 Test num과 LC/RC 부분에 몇 회차 테스트의 단어를 공부할 것인지 LC인지 RC인지 설정할 수 있게 되어있다.
위 결과들은 절대로 본인이 섞어서 올린 것이 아닌 순전히 매크로를 이용해서 출력된 결과물임을 밝히겠다. LC뿐만 아니라 RC 또한 작동되고 Test 4뿐만 아니라 다른 회차도 작동되지만 아직 단어를 추가하지 않아서 동작하지는 않는다.
아래 링크로 들어가서 본인의 spreadsheet로 복사해서 직접 실험해볼 수도 있다.
매크로 사용법은 [ ctrl + alt + shift + 1 ] 이다.
[개선하거나 추가할 기능들]
- 영단어 뿐만 아니라 한글 뜻도 영단어처럼 랜덤으로 시험지에 추가하는 기능
- 시험지에 답을 적으면 자동으로 틀렸는지 맞았는지 검사해서 점수까지 내주는 기능
- 시트 레이아웃을 좀 더 세련되게 꾸밀 것
- 시험지 시트 왼쪽 상단에 Test num 선택란에서 회차를 선택하면 해당 회차의 시작 셀로 자동으로 옮겨가는 기능
[느낀점]
프로그램의 핵심 알고리즘인 중복값 없이 랜덤한 값을 뽑는 알고리즘은 이해하기 쉬웠지만 그걸 실제로 단어장에 적용하는 과정에서 이 값이 실제 데이터인지 아니면 인덱스인지 구분하는 것이 어려웠다.
Javascript와 Google Apps Script가 굉장히 유사하다는 사실을 알게되었다. 이 언어를 쓰면서 타입을 정하지 않고 그냥 var로 받는 부분이나 두 객체나 변수, 상수를 비교할 때 ==로 형 변환을 해서 비교하는 부분, ===로 형 변환을 하지 않고 비교하는 부분이 javascript와 굉장히 유사하다고 느껴졌다. 또한 Arrays class의 메소드들과 문자열 메소드들이 완전히 동일하다는 점도 유사한 부분으로 꼽을 수 있겠다.
또한 Google Apps script는 예전에 군대에서 한셀을 사용할 때 써본 VBScript와 비슷하여 이해하는 것은 쉬웠지만 아무래도 잘 사용하지 않았었고 이번에 이렇게 많은 소스코드와 복잡한 절차를 한꺼번에 기술하는 과정에서 발생하는 여러 오류들과 오동작들을 잡는 것이 굉장히 까다로웠다. 이런 이유로 프로그램을 완성하는 데 12시간이나 걸렸다.
주로 사용하진 않는 언어이지만 그래도 스프레드시트를 한층 더 편하게 사용할 수 있게 해주는 언어이기 때문에 까먹지 말고 앞으로도 유용하게 써야겠다.
또한 이제 제대로 토익 공부를 할 수 있을 것 같다.
[직접 사용해볼 수 있는 링크]
docs.google.com/spreadsheets/d/1V3xxGVhScHk6aGU2mcW_riojksfy9LFMDUlI1Zw1MQk/edit?usp=sharing
'Google Spreadsheet > Google Apps Script' 카테고리의 다른 글
getValues() 메소드의 함정 (0) | 2020.12.03 |
---|