신문 뒷면 낱말퀴즈를 내 손으로 만든 날
예전엔 신문 맨 뒷면에 낱말퀴즈가 꼭 하나씩 실려 있었잖아. 지하철에서 심심할 때 볼펜 하나 들고 칸 채우던 기억, 나만 있는 거 아니지? 모른다고? 라떼는 다 그랬어. 아무튼 이번에 그 낱말퀴즈를 직접 만들게 된 계기가 있어.
발단은 맞춤법이었어. 블로그 글을 매번 쓰다 보니 맞춤법이 은근히 신경 쓰이더라고. 뭐 좋은 방법 없나 찾아보다가 국립국어원이 표준국어대사전을 오픈 API로 공개해놨다는 걸 알게 됐어. AI 조카한테 바로 물어봤지.
"이걸로 글 올리기 전에 맞춤법 검사에 쓸 수 있어?"
"삼촌, 아쉽게도 국립국어원엔 맞춤법 검사 API 자체가 없어. 이건 단어가 사전에 있는지, 뜻이 뭔지만 확인해주는 순수 조회용이야."
음, 아쉽긴 한데 이왕 발견한 거 어떻게든 사용해보고 싶었어. 그러다 출퇴근길에 낱말퀴즈 풀던 옛날 생각이 스쳤고 그렇게 맞춤법 검사기를 찾던 길이 게임 만들기로 방향을 틀었지.
저작권부터 확인했다 — 표준국어대사전 API 상업적 이용
근데 코드부터 짜자니 뭔가 찜찜했어. 이 블로그에 애드센스를 붙여 보고 싶은데 국립국어원 사전 데이터로 만든 게임에 광고를 달아도 되는 건가 싶더라고. "돌다리도 두드려 보고 건너라." 나이 먹은 사람들은 조심성이 원래 많아.
AI 조카가 표준국어대사전 저작권 정책 페이지를 직접 읽고 정리해줬어.
"삼촌, 이거 CC BY-SA 2.0 KR이야. 상업적 용도까지 포함해서 누구나 자유롭게 쓸 수 있다고 명시돼 있어. 애드센스는 물론이고 이 게임을 팔아도 라이선스상 문제없어."
대신 조건이 두 개 붙는다고 했어. 하나는 저작자 표시, 뜻풀이 출처: 국립국어원 표준국어대사전이라고 게임에 밝혀야 한다는 것. 다른 하나는 동일조건변경허락이라는 거였어.
"삼촌, 사전 뜻풀이를 내가 그대로 안 쓰고 다듬어서 단서로 낼 거잖아. 그럼 그것도 2차 저작물이라 원본이랑 같은 조건(CC BY-SA)으로 공개해야 돼."
AI가 다듬은 결과물에까지 원본의 조건이 따라붙는다는 게 신선했어. 대신 예문은 쓰지 말고 뜻풀이만 쓰기로 했지. 용례는 원저작자 권리가 따로 있다고 하더라고.
키 발급은 본인 명의 휴대폰 인증까지 필요해서 내가 직접 했어. 이건 AI 조카가 대신해줄 수 없는 부분이었으니까.
한 칸에 두 글자, 한글 IME가 만든 함정
키를 손에 쥐고 나서 처음 만든 건 가짜 문제로 채운 뼈대였어. 격자 칸을 클릭하면 그 칸에 바로 글자를 치는 제일 직관적인 방식으로 짰지. 근데 실제로 타이핑해보니 사달이 났어. 한 칸에 글자가 두 개씩 박히는 거야.
AI 조카한테 물었더니 원인이 의외였어.
"칸마다 최대 한 글자만(maxLength=1) 걸어놨는데, 한글은 그게 안 먹혀. 자음+모음을 조합하는 중에 글자가 재구성되거든."
예를 들어 '가'를 쳐놓고 'ㄴ'을 누르면 '간'이 되고, 거기서 다시 모음을 치면 받침 ㄴ이 다음 음절로 넘어가서 '가나' 두 글자가 돼버려. 영어라면 한 글자씩 딱딱 끊기는데, 한글은 칠 때마다 통째로 재조립되는 거였지.
한 번 고쳤는데도 안 됐어. 조합이 끝나는 시점을 잡아서 처리했더니, 실제 내 타이핑에선 여전히 두 글자가 박히더라고. 두 번째로 짚어낸 원인은 더 미묘했어. 한 칸에서 연속으로 치면 조합이 끊기지 않고 계속 이어져서 조합 끝났다는 신호 자체가 안 온다는 거였어. 코드가 실행될 타이밍 자체가 안 생긴 거지.
두어 번 손봤는데도 안 되니까 슬슬 짜증이 났어. 무한정 코드를 돌리더라고. (내 토큰!!!)
"코드 그만 고치고 다른 방법을 알려줘. 오류가 너무 많아."
되는 데까지 파고드는 것도 방법이지만 가끔은 방향을 트는 게 더 빠를 때가 있잖아.
AI 조카가 아예 다른 답을 들고 왔어.
"칸마다 입력창을 두는 구조 자체가 문제야. 한글 조합은 이어져야 하는데 칸 경계가 자꾸 그걸 끊어버리거든."
대안으로 세 가지를 제시했어. 단어를 통째로 입력창 하나에 치고 칸에 나눠 뿌리는 방식, 화면에 자모 키보드를 따로 두는 방식, 후보 글자를 골라 넣는 방식. 나는 첫 번째를 골랐어. 격자 아래 입력창을 두고, 단어를 선택하면 해당 칸에 입력되는 구조로.
바꾸고 나니 지금까지의 버그가 한 번에 사라졌어. 입력창이 하나뿐이니 한글 조합이 안에서만 완결되고, 칸 경계를 넘나들 일도 없었던 거지. 어쩌면 처음부터 이렇게 하는 게 맞았을지도 몰라. 보기에도 좋더라고.

표준국어대사전이 직접 문제를 검수하기 시작했다
입력 구조가 자리 잡고 나니 진짜 재료인 사전 API를 붙일 차례였어. 단어 은행에서 후보를 고르고 격자에 백트래킹으로 채워넣은 다음, 격자에 생긴 모든 가로·세로 줄을 표준국어대사전 API로 한 줄씩 전수 검증하는 거야. 사전에 없는 단어면 그 배치는 버리고 다시.
"사람이 손으로 짜면 실수로 유령단어가 섞일 수 있는데, 사전이 매번 확인해주니까 가짜 단어가 나올 확률이 0이 되는 거야."
AI 조카 말이 딱 맞았어. 재미(밀도 있는 격자)랑 정확성(진짜 단어만)을 동시에 잡은 셈이었지.
근데 단서를 자동으로 뽑으니 또 다른 함정이 나왔어. '지구'를 조회했더니 첫 번째 뜻이 우리가 아는 행성이 아니라 고구려 때의 춤곡이었어. '나비'도 곤충이 아니라 피륙의 너비가 먼저 나오고. 처음 알았지 뭐야. 사전은 늘 옛말이나 전문 용어부터 순서대로 나열하고 우리가 흔히 쓰는 뜻은 저 뒤에 숨어있는 경우가 많았던 거야.
"삼촌, 단어가 진짜 있는지는 사전이 확인해주지만 어떤 뜻으로 쓸지는 내가 골라야 돼." AI 조카가 정리해준 역할 분담이 이거였어. 존재 검증은 기계가, 의미 큐레이션은 사람(정확히는 AI)이. 국어사전을 그대로 베끼면 안 되고 흔한 뜻으로 다듬어야 한다는 걸 이때 처음 체감했어.
47만 번의 해없음 끝에 뒤집은 것
판을 키우면서 제일 극적인 사건이 터진 것도 이 무렵이야. 5×5 자그마한 판에서 8×8로 키우려는데 격자를 채우는 방식 자체를 손봐야 했어.
블록 배치(칸 모양)를 먼저 정하고 거기 맞는 단어를 나중에 끼워 맞추는 방식. AI 조카는 이걸 grid-first라고 불렀어. 근데 8×8에서는 아무리 손봐도 해가 없다는 응답만 나왔어. 그것도 한두 번이 아니라 47만~54만 번을 시도하고서도 말이야.
그런데 딱 봐도 답이 나오지 않아? 내가 보기엔 그랬거든.
"슬롯을 먼저 만들지 말고, 단어 조합을 먼저 하면 되는 거 아니야?"
이랬더니 녀석이 말하길,
"그게 word-first야, 크로스워드 만들 때 정석으로 쓰는 방식이거든."
"그럼 바꾸면 되잖아?"
"그래"
"..."
바꾸고 나니 결과가 허무할 정도였어. 47만 번을 시도해도 안 되던 게, 방향을 뒤집으니 9번에서 500번 사이 시도로 1.8초 만에 성공했어. 긴 단어를 먼저 시드로 놓고, 거기 교차되는 단어를 하나씩 붙여가며 격자를 키우는 거지. 이러면 애초에 항상 성립하는 조합만 쌓이니까 해없음 자체가 나올 수가 없어.
최적화를 더 세게 밀어붙일 게 아니라, 문제를 정의하는 순서 자체를 뒤집어야 했던 거야.
놀라울 만큼 똘똘한 녀석이 이런 쪽에서 해법을 찾지 못하다니. 아니면 내가 지시를 잘못한 건가? 아무튼 이 과정은 녀석과 얘기를 해서 비슷한 상황이 나오면 같은 실수를 하지 않도록 짚고 넘어갔어. 그래도 모르지. 나중에 또 어떤 일이 있을지 말이야.
강하게 미는 것보다 애초에 방향이 맞았는지 먼저 보라는, 코딩 밖에서도 써먹을 법한 교훈을 여기서 하나 건졌어.

오늘의 결론
지금까지는 "다음엔 뭘 만들까"부터 골랐잖아. 이번엔 순서가 거꾸로였어. 맞춤법 하나 신경 쓰다가 발견한 API가 게임이 되었으니까. 남의 데이터를 가져다 쓰려니 저작권까지 신경 써야 했고, 사전이 검수관 역할까지 해줬어. 생활 속에서 걸리적거리던 게 재료가 될 수도 있다는 걸 새삼 깨닫게 됐어. 여러모로 의미있는 과정이었단 생각이 드네.
완성된 십자말풀이는 여기서 바로 플레이해볼 수 있어. 완성이 되기까지 겪은 디자인 개편이랑 또 다른 버그와 배포 이야기는 다음 글에서 마저 풀게.


