-
러브라떼 변환기 개발일지토이 프로젝트 2022. 3. 19. 05:15
러브라떼라는 귀여운 척을 넘어선 해괴한 말투로 유명한 빌런(?)이 있다.
요즘 안보이던데, 메이플 접었나?
최근 간단한 웹사이트 만드는데 재미가 들려서
이런 짤방 생성기나, 듀자이크 생성기를 만들었는데,
옛날에 만들려다가 때려친 러브라떼어(?) 변환기를 한번 만들어보고자하였다.
정상적인 문장을 입력하면 윗 글처럼 바꿔주는 프로그램이다(...)
옛날에는 주로 배운게 C라 C언어로 만들려고 했는데
C언어는 배열에 한글을 넣고 쓰는게 더럽게 불친절해서 때려쳤었다.
예를들면 "시공조아"란 문장을 나눈다면 시/공/조/아 4칸으로 나눠질것같지만
각 글자가 3조각씩, 총 12칸으로 나눠진다.
그렇다고 "공"이란 글자가 ㄱ//ㅗ//ㅇ 이렇게 깔끔하게 나눠지지도 않는다.
자바스크립트는 시/공/조/아 4칸으로 나눠지므로
쉽게 구현 가능하다!
대충 봤을때, ㅏㅓㅗㅜ 같은 단모음만 ㅑㅕㅠㅛ로 바꾸면 되겠지?
한시간도 안걸리겠다하고 가벼운 맘으로 시작했는데
인생은 마음대로 되지 않는법이란다 꼬마야
"롤인"벤 -> "로린"벤
"들아" -> "듀랴"
"전하"러 -> "져냐"려
망할 연음법칙
망할 훈민정음
ㅏ를 ㅑ로 바꾸려면
가나다라마바사...를 갸냐댜랴먀뱌샤로 바꾸고...
개노가다긴 하지만 머리쓸일없으니까,
대가리 굴리기 귀찮아서 그냥 노가다 손코딩으로 때울려고했는데
연음법칙이 생기면 말이 달라진다
"롤인"을 "로린"으로 바꾸고
"들아"를 "듀라"로 바꾸고...
고쳐야 할게 제곱이 됐는데
이건 노가다로 해결할 수 있는 수준이 아니다.
결국 대가리를 쓰기로 했고
뜻밖의 여정이 시작됐다...
일단 코딩에는 "배열"이라는 개념이 존재한다.
쉽게 말해 학교 출석부같은 느낌이다.
1번은 이재명이고
2번은 윤석열이다
1번 칸에는 "이재명"이란 이름이,
2번 칸에는 "윤석열"이란 이름이 들어가있다.
그렇게 3번칸에는 "안철수", 7번에는 "허경영"....
특정 값들을 순서에 따라 저장해 놓은 리스트라고 보면 된다.
주의사항이 있다면 "대선후보"라는 배열이 있다면
"대선후보[0]"이 이재명 "대선후보[1]"이 윤석열,
배열의 칸은 0번칸부터 시작된다.
그리고 이 배열에 문자를 하나씩 넣은걸 "문자열"이라고 한다.
언어에 따라 조금 다르긴한데 대충 그렇다.
배열의 한 칸에 "시공조아"라는 글자 4개를 전부 넣을수도있겠지만
첫번째칸에는 "시" 두번째엔 "공", 세번째엔 "조", 네번째엔 "아"를 집어넣는다.
자 그럼 이제 라떼 변환기를 구현하려면....
1.변환할 문장을 입력받는다.
2.입력받은 문장을 "문자열"로 저장한다.
3.해당 문자열을 자음,모음으로 나눠 "배열"로 저장한다
4.해당 배열에서 값을 수정하여 라떼어(?)로 만든다
5.수정이 끝난 배열을 합쳐 다시 "문자열"로 만든다
6.해당 문자열을 출력한다
"시공조아"를 입력받았다면
"시공조아"를 "ㅅ ㅣ ㄱ ㅗ ㅇ ㅈ ㅗ ㅇ ㅏ"로 바꾸고
ㅗ를 ㅛ로, ㅏ를 ㅑ로 바꾸는 코드를 넣어서
"ㅅ ㅣ ㄱ ㅛ ㅇ ㅈ ㅛ ㅇ ㅑ "
로 바꾸고 이를 합쳐서
"시굥죠야"를 만들어서 사용자에게 보여준다.
이렇게 자음, 모음을 나누면
시"공"조아의 받침 ㅇ과
시공"조"아의 윗자음 ㅈ이 붙어있는 것을 알수있다.
즉 자음 2개가 붙어있을때, "연음 -> ㅇ ㅕ [ㄴ ㅇ] ㅡ ㅁ"
앞 자음이 ㅇ이 아니고 뒷 자음이 ㅇ 이라면 [ㄴ ㅇ]
앞 자음을 없애고 뒷 자음을 앞에 값으로 바꾸면 [ ㄴ]
연음법칙을 구현할 수 있다! "ㅇ ㅕ ㄴ ㅡ ㅁ -> 여늠"
이 외에도 ㅏ를 쓰는 글자인
가를 갸로, 나를 냐로, 다를 댜로,
하나하나 일일히 바꿔야 했던 기존 방식과 달리
그냥 배열을 쭉 훑으면서 "ㅏ" 글자가 있으면 "ㅑ"로 바꿔버리면 되니 훨씬 간단하긴 하다.
글자를 모음 자음으로 나누는걸 구현하는게 대가리 아파서 그렇지.
그래서 숙련된 코붕이는 이제 무엇을 하느냐
일단 구글부터 뒤진다.
내가 만들려는건 누군가가 이미 만들어놨을 확률이 굉장히 높다
러브라떼 변환기를 만드려는 새끼는 흔치 않겠지만
한글 문자열을 자음 모음으로 분해하는 코드를 구현한 사람은 충분히 많을것이다.
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=tk2rush90&logNo=221085154547
[자바스크립트] 한글 자음 모음 분리 / 초성 * 중성 * 종성
0. 서문한글 자음모음 분리 기능을 이용할 일이 생겨서 알아보았다. 핵심함수는 charCodeAt( ). 어렵진 ...
blog.naver.com
그렇게 정말 좋은 글을 찾았다.
코드를 바로 복붙해와서 내 용도에 맞게 변경하면 된다.
일단 해당 방법은, charCodeAt()이란 메서드를 사용한다
이게 뭔지는 몰라도 된다. 나도 오늘 초면이니까
해당 메서드는 문자를 고유한 숫자 값으로 변경해준다.
"가"를 넣으면 44032라는 숫자를,
"걌"을 넣으면 44108이란 숫자를,
넣는 글자에 따라 숫자가 바뀌고,
같은 숫자를 넣으면 같은 값이 나온다.
그리고 이 값을 활용하는 방법은,
해당 숫자값에서 44032를 뺀 다음 28로 나눈 뒤 나머지를 확인해보자
44032에 44032를 빼면 0이고 이를 28로 나누면 나머지는 당연히 0이다
이는 받침 글자가 없음을 의미한다
44108에서 44032를 빼면 76이고, 이를 28로 나눈 나머지는 20이다.
이는 받침 글자가 20번째 받침글자, "ㅆ"임을 의미한다.
또 44032와 44108에서 44032를 뺸 0과 76을 588로 나눴을때 몫은 둘다 0이다.
이는 둘다 윗 자음이 0번째 자음, "ㄱ"임을 의미한다.
(위에서 말했듯 배열은 0부터 시작한다.)
즉 "걌"에 해당하는 숫자에 정해진 수식계산을 행하면 각각
0, 2, 20이란 값이 나오고
이는 각각 0번째 자음, 2번째 모음, 20번째 자음을 의미하여
이후 행한 수식계산을 반대로 재적용해서
0*588 + 2 * 28 + 20 = 76.
"걌"의 고유값에서 44032를 뺸 수치로
이를 통해 ㄱ, ㅑ, ㅆ은 이후 다시 합쳐져 "걌"이 된다.
즉 문자열의 글자중 하나인 "걌"을 0,2,20이란 3개의 값으로 분리해 배열에 순서대로 저장하고,
이 배열을 사용해 각 글자의 특정 모음에만, 혹은 특정 자음에만 변화를 주고
다시 이 배열을 순서대로 3칸씩 읽어 합치면 수정된 문자열이 완성된다.
이제 슬슬 코드를 살펴보자.
<br> <textarea id="before" cols="100" rows="10"></textarea> <br> <button onclick ='love()' >변환</button>
전체 코드의 html, 즉 겉으로 보여지는 부분이다.
보통 웹사이트는
html, css, 자바 스크립트 3개로 나눠지는데
html은 버튼 그 자체를
css는 버튼을 어떻게 꾸밀지를,
자바 스크립트는 버튼을 누르면 어떠한 일이 발생하는지 설정한다
textarea는 위에 큰 박스로
라떼어(?)로 바꿀 글을 입력한다.
밑에 변환 버튼은 누르면 위의 textarea에 입력된 내용을 라떼어로 바꿔 출력한다.
코드를 살펴보면 onclick='love()'란게 있는데
onclick, 즉 클릭하면 love()라는 함수를 실행하란 뜻이다.
함수는.. 예를 들자면 "자동요리기계" 같은거다.
해당 기계로 떡볶이를 만들려면,
일단 요리기계 안에 떡, 오뎅, 고추장을 넣는다.
이것이 함수에서 처리할 데이터로, 매개변수나 인자라고 한다.
함수처리는 요리과정이다
오뎅을 썰고 물과 떡 고추장을 넣고 끓인다
이때 오뎅을 썰고, 고추장을 푸는 등 요리 과정을 우리가 구현해야한다.
그리고 반환값은 넣은 데이터와 처리과정을 거치고 나온 결과값, 즉 떡볶이다.
아까 html은 버튼을 구현하고,
javascript는 이 버튼을 누르면 어떻게 될지 구현한다고 했다.
html에서는 "이걸 누르면 love() 함수가 실행된다" 라는것만 써놓고,
해당 love()함수는 자바 스크립트에서 구현하게된다.
이제 자바 스크립트를 보면
-여담으로 하나도 안꾸며서 css는 없다(...)-
function aaf(kor) { const f = ['ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ']; const s = ['ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ']; const t = ['', 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ']; 뒤에 이어서
일단 아까 블로그에서 본 함수를 복붙해왔다.
인자(재료)로 "뷁"이란 한 글자를 넣으면 이를 자음, 모음으로 분리해 (요리과정)
ㅂ ㅞ ㄺ 3개의 값 (요리)를 리턴한다.
정확히는 ㅂ이 7번째 자음이니 ㅂ이 아니라 7을,
ㅞ가 16번째 모음이니 16을,
ㄺ이 9번째 자음이니 9를 리턴한다.
let fn=77; let sn=77; let tn=77; let uni = kor.charCodeAt(0); if(uni>=12593 && uni <12623){ tn=uni-44032; } else if(uni>=12623 && uni <12644){ tn=uni-44032; }
그대로 복붙해온건 아니고,
"ㄱ" "ㅏ"와 같은 모음이나 자음만 입력한 경우의 처리는 내가 직접 추가했다.
뷁은 ㅂ ㅞ ㄺ = 7 16 9 로 바꿨는데
ㄱ은 어떻게 바꿔야할까? 1 0 0?
배열은 0부터 시작이라 1 0 0은 ㄴ + ㅏ +(받침없음) = "나"를 의미한다.
즉 좀 다르게 저장해야하는데
나는 77 77 0 으로 저장했다. *(정확히 말하자면 0은 아닌데 편의상 0이라함)
0이 ㄱ을 나타내는 값이고
77 77은 이후 마지막에
해당 글자가 모음 하나 혹은 자음 하나로만 구성되있음을 구별할때 사용한다.
77번째 모음이나 자음은 없으므로, 해당 값은 자유롭게 사용가능하다.
else{ uni = uni - 44032; fn = parseInt(uni / 588); sn = parseInt((uni - (fn * 588)) / 28); tn = parseInt(uni % 28); } br[index]=fn; br[index+1]=sn; br[index+2]=tn; index+=3
그 외의 경우, 즉 "뷁"과 같은 글자가 제대로 입력된 경우는 원래의 함수대로 처리하고,
마지막으로 이렇게 결정된 3개의 숫자 값을 순서대로 배열에 저장한다.
위 코드의 변수 index의 맨 처음 값은 0이고,
힘수가 처음 실행될때
br[index] br[index+1] br[index+2] 즉
br[0] br[1] br[2]에 값을 순서대로 채우고, 이후 인덱스의 값은 3이된다.
그 다음 함수가 실행될때는 인덱스가 3이 됐으므로
br[index] br[index+1] br[index+2]는
br[3] br[4] br[5]이 되어 순서대로 데이터가 차고,
또 인덱스가 3 증가해 6이 된 뒤
br[6],br[7], br[8]... 이렇게 배열을 순서대로 채워나간다.
참고로 index +=3 은 index = index+3 이란 뜻이다.
코드가 짧을때야 왜 굳이 저렇게 쓰나 싶었다만,
float(abs(br.length))) 같이 변수가 길어질수록 앞 방식이 편해진다.
아 이것도 설명해야 하나
코딩에서 "="는 둘이 같다라는 뜻이 아니다.
이는 =의 왼쪽에 있는 값을
=의 오른쪽에 있는 값으로 바꾼다는 뜻이다.
즉 둘이 같다가 아니라 "같게한다"라는 뜻이다.
둘이 같은지 확인할때는 =이 아니라 ==를 쓰고,
코딩을 처음배울때 저것때문에 많이 헷갈린다.
아 그리운 옛시절이여
function love(){ var txtarea = document.getElementById('before');
자 이제 love() 함수를 살펴보자.
love 함수는 "변환" 버튼을 눌렀을때 실행되도록 되어있던 함수로,
textarea에서 입력받은 문자열 값을 전달받아 (위 코드)
문자열의 한글자씩 위에서 설명한 aaf함수(글자를 모음, 자음으로 분리하느 함수)
에 집어넣어 분리된 3개의 값을 수선대로 배열에 저장한다
이 배열의 값에 라떼어(?) 변환 과정을 거치고
최종적으로 완성된 배열을 합쳐 문자열로 만든뒤 이를 출력하는,
라떼 변환기에 필요한 모든 기능을 담당하고있다.
즉 라떼 변환기의 함수들은
전체적인 프로그램 실행을 진행하는 love()함수와
그 love()함수 안에서 특정 기능을 수행하는 함수들로 나뉜다.
var contents = txtarea.value.replace(/(\n|\r\n)/g, 'n');
replace는 문자열의 특정 글자를 바꾸는 메서드이다.
"시공조아"라는 문자열에서 "조"라는 글자를 "좋"으로 바꿀려면
"시공조아".replace(/조/gi, "좋");이란 코드를 사용하면 된다.
위는 좀 특수한 경우라 코드가 살짝 다른데,
특정 글자가 아닌 줄바꿈=엔터를 바꾸는 코드이다.
우리가 문자열을
"시
공조아"
이렇게 써도 컴퓨터는 이를 다르게 인식한다.
엔터는 주로 \n으로 나타낸다.
즉 위 문자열을 입력받아 다시 출력하면
"시
공조아"
가 아닌
"시\n공조아"로 출력된다
위 코드는 \n을 일단 "n"이란 한 글자로 바꿔놓는 코드이고,
이후 뒤에서 이를 다시 처리할 예정이다.
var은 contents라는 변수를 사용하겠다 "선언"한다는 뜻이다.
contents = contents.replace(/ /gi, "s");
공백도 따로 처리하기 위해 s라는 한 글자로 바꿔놓는다
저걸 특수기호로 처리했어야했는데, s로 해놔서
영어 s를 입력한게 스페이스로,
영어 n을 입력한게 엔터로 인식되는 버그가 발생했다.
"동햬뮤ㄹ과 뱩듀샤니 먀르고 댜ㄹㄷ로고~!><♥️"
물론 결과물이 이따구라
버그가 나도 눈에 잘 안띈다.
그래서 나도 고쳐야지 해놓고 까먹고있었다.
글 다 쓰고 고쳐야징
contents = contents.replace(/들/gi, "듀랴"); contents = contents.replace(/도/gi, "듀"); contents = contents.replace(/와/gi, "오ㅏ");
그 외에는 "들"이라던가 "도"로던가 "와"라던가
자주쓰는 글자들 일부를 수정해줬다.
오빠들은 오빠듀랴로 바뀌게된다.
보다보면 소름돋는게
"들"이란 글자는 체게적으로 "듀랴"라고 썼더라.
이거 진짜 규칙이 있을지도 몰라...
for(i=0 ; i<contents.length ; i++){
이제 for문, 반복문의 시작이다.
예를 들어 집이 30개 있고, 30 곳에 전부 들려야 된다고 생각해보자
1번 집에 들리는 코드,
2번 집에 들리는 코드,
3번 집에 들리는 코드...
이렇게 비슷한 코드를 30번을 쓰는건 너무 비효율적이다.
그렇기에 i번째 집에 넣는 코드를 만들어놓고
이 i값에 1부터 30이란 값을 순차적으로 넣는 반복문, for문을 사용한다.
이 for문은 보통 배열의 값에 순서대로 반복하여 접근하는데 사용된다
배열의 0번 값에 접근하고, 1번값에 접근하고...
for문은 3가지로 구성되는데
1.초기식
반복의 기준으로 사용할 변수를 지정한다.
2.조건식
조건이 만족할때까지 반복한다
3.변화식
한번 반복문이 실행된 다음 실행되는 코드이다.
위 코드를 기준으로 하면
i값은 0이고 hello, world!가 출력된다.
그 다음 i++이 실행된다.
++은 값을 1 늘린다는 뜻이다.
즉 i는 1이 되고, 또 hello, world!가 출력되고, i가 또 1 늘어 2가 되고...
i가 99가 되고, 출력하고, i가 1 커져 100이 되면 이제 조건을 만족하지 않으므로 더이상 실행되지 않는다.
for(i=0 ; i<contents.length ; i++){
나는 이 for문을 contents.length만큼 반복한다.
length는 문자열이나 배열의 길이를 리턴하는 "프로퍼티"이다.
프로퍼티는 객체가 지닌 정보라고 보면 된다.
배열을 생성하고 내용을 추가하다보면,
그 배열의 길이가 몇인지 해당 배열 안에 정보가 저장된다.
이 정보를 가리키게 하는게 프로퍼티다.
객체가 지닌 정보인 만큼 정보는 객체에 따라 다르다.
배열이나 문자열이면 길이가 주 정보가 될꺼고
이미지면 너비와 높이
텍스트면 텍스트 크기 등
만약 시공조아를 넣었다면 이는 4글자이므로 "시공조아".length 는 4이다.
즉 위 코드는 문자열을 처음부터 끝까지 한글자씩 보겠다 라는 뜻이다.
if(ishangul(contents[i])){ aaf(contents[i]); }
contents[i], i는 반복문에서 기준으로 사용하겠다 지정한 변수이므로,
i는 반복문이 반복될떄마다 1씩 증가하고,
함수에contents[0], contents[1], contents[2]의 값을 순서대로 넣는다.
contents가 "시공조아" 라면 "시", "공", "조"를 순서대로 넣는것이다.
일단 ishangul이란 함수에 해당 값을 넣었는데
이는 인자(재료)로 넣은 값이 한글인지 아닌지 확인하는 함수이다.
function ishangul(ch) { var c = ch.charCodeAt(0); if( 0x1100<=c && c<=0x11FF ) return true; if( 0x3130<=c && c<=0x318F ) return true; if( 0xAC00<=c && c<=0xD7A3 ) return true; return false; }
역시나 이런건 누군가 구현해놨으므로 구글에서 슬쩍한다.
넣은게 한글이면 true를 반환하는 함수로
해당 함수가 if문 안에 있으므로
true를 반환했을때 = 한글일때
if문의 조건이 성립되어
aaf 함수에 해당 값을 다시 한번 넣는다.
aaf는 위에서 설명한 한글을 모음, 자음으로 분리하는 함수다.
즉 한글이 아닌값은 넣으면 오류가 날테니 걸러내는 것이다.
if문은 if(조건) 코드;
의 구조로 if( )안의 조건이 true면 뒤의 코드가 실행,
아니면 생략된다.
else if (contents[i]=="s"){ br[index]="s"; br[index+1]="s"; br[index+2]="s"; index+=3; }
if 다음은 else if다.
else if는 앞에 if문의 조건이 만족하지 않았을때 이 조건이 만족하면 작동하다라는 뜻이다.
만약 a=2고 b=3이라면
if(a==2) az();
else if (b==3) bz();
에서 az는 실행되지만 bz는 실행되지 않는다.
if(a==2)에서 a==2가 true라 조건문이 실행되 시점에서
그 밑에 else if 와 else는 바로 스킵된다.
if(a==2) az();
if (b==3) bz();
이렇게 쓰면 두 if문 사이에는 아무런 연관이 없어서,
앞의 if가 성공하든 말든 뒤 if문이 조건이 true면 실행된다.
즉 az() bz() 모두 실행된다
else if (contents[i]=="s"){ br[index]="s"; br[index+1]="s"; br[index+2]="s"; index+=3; }
쨌든 위 코드는 문자열의 값이 "s"일떄,
즉 아까 띄어쓰기 공백을 지정해놓은 값이다.
이때 br 배열에는 "s"의 값을 3개 넣는다.
br 배열은 "뷁"을 ㅂ ㅞ ㄺ으로 나눠 숫자 3개로 저장해놓은 그 배열이다.
해당 배열에서 값을 3개씩 읽어 하나의 글자로 합치므로,
무조건 br배열엔 값을 3개씩 저장해야한다.
else if(contents[i]=="n"){ br[index]="n"; br[index+1]="n"; br[index+2]="n"; index+=3; }
n은 엔터 = 공백.
위의 s와 똑같으니 설명은 생략
else{ el[ei]=contents[i]; br[index]="e"; br[index+1]="e"; br[index+2]="e"; index+=3; ei+=1; }
마지막은 else문이다.
앞의 if와 else if문이 전부 실패했을때 발동되는 조건문이다.
즉 위 코드에선
읽은 문자열의 값이 한글도, s=스페이스도, n=엔터도 아닐때이다.
특수문자와 다른 영어등이 포함된다.
이때는 "e"라는 값을 저장하기로 한다.
별 뜻은 없고 그냥 else에서 따왔다.
혹시나 해서 말하자면
n, s, e 라는 단어에 특별한 기능이나 의미가 있는게 아니고,
그냥 아무 알파벳이나 쓴 다음
이후 이 알파벳을 조건문에 넣어
뒤에서 해당 문자가 공백인지, 엔터인지 체크할것이다.
for (j=0 ; j<index ; j+=3){
그 다음은 2번째 for문이다.
이번엔 기준이 되는 변수가 j이며,
똑같이 0부터 시작한다.
하지만 한번 반복될때마다 3씩 증가하며
index 값보다 커지면 종료한다.
index는 br배열에 값을 3개 넣을때마다 3씩 증가시킨 값으로
이는 즉 br배열의 길이와 같다
또 말하자면 br배열은 뷁을 ㅂ ㅞ ㄺ으로 나눠 저장한 그 배열이다.
쨌든 이 br배열에 한번에 3칸씩 접근하겠다는건
한 글자의 윗자음, 모음, 받침자음에 한번에 접근하겠다는 뜻이다.
지금짜기 입력받은 문자열을 자음 모음으로 분리했고
이제 분리된 자모음들을 러브라떼 어법(?)에 맞게 수정시킬것이다.
if(br[j+1]!=77){
!=는 ~가 아닐떄라는 뜻으로 ==와 반대이다
즉 br[j+1]의 값이 77이 아닐때 실행된다.
br[j]는 윗자음
br[j+1]는 모음
br[j+2]는 받침자음이다.
77은 기억하고 있다면
아까 위의 자음 모음을 분리하는 함수에서
"뷁" 같은 온전한 단어 대신
"ㄱ" "ㅏ"와 같은 모음, 자음만 입력했을때
이를 구별하기위해 넣어둔 값이였다.
위와 같이 다른 값과 겹치지 않는 임의의 값을 정해서 넣어두고
이를 뒤에서 if문으로 확인해 선별하는 방식을 나는 주로 쓴다.
if(br[j+2]==9||br[j+2]==3) br[j+2]=1; else if (br[j+2]==6) br[j+2]=4; else if (br[j+2]==13 || br[j+2]==11) br[j+2]=7; else if (br[j+2]==15) br[j+2]=8; else if (br[j+2]==10) br[j+2]=16; else if (br[j+2]==14) br[j+2]=26;
코드가 더럽지만 양해부탁
이는 복잡한 받침글자들으 처리하기위한 코드이다
"않"이라는 글자가 있다면
ㄶ을 ㄴ으로 바꿔버린다.
ㄶ은 6번째 받침 자음이고,
ㄴ은 4번째 받침 자음이므로
else if (br[j+2]==6) br[j+2]=4;
위의 여러개의 else if문중 이 파트가 해당 코드이다.
이게 단어의 발음을 나타내는 프로그램이라면
연음일때를 따져 이리 바꾸고 저리 바꾸고 해야겠지만
까먹었을수도 있겠지만 이건 러브라떼 변환기다(...)
귀찮은 받침글자는 대충 처리해버리자
이건 tmi인데
else if (br[j+2]==6) br[j+2]=4;
를
else if (br[j+2]=="ㄶ") br[j+2]="ㄴ";
이런식으로 했다면 훨씬 가독성이 좋고 이해가 빠를것이다.
다만 아~까 설명했듯이
모음 자음으로 분리하거나 합칠떈 숫자 상태로 진행해야하기에
가 => 43022 => 0 0 0 => 43022 => 가
이런 과정을 거치는데 위처럼 문자로 표현하려면
가 => 43022 => ㄱ ㅏ => 0 0 0 => 43022 => 가
하나의 과정을 더 거쳐야한다.
나의 경우 어차피 혼자 만든 프로그램이니까 모든 코드 내용을 내가 직접 쓰거나 수정했고,
즉 모든 내용을 알고 있으니 변수를 어렵고 복잡하게 써도 상관없지만
다른사람과 협업하는게 보통인 개발판에선
남들이 이해하기 쉽게 짜는게 중요하다.
그걸 위해 ㄱ ㅏ 를 0 0 0으로 바꾸는 코드를 추가로 짜야한다해도말이다
tmi 끝
if(br[j+2]!=0 &&br[j+2]!=21 && br[j+3]==11 && br[j+1]!=77){
다음 조건문이다.
br[j+2] != 0 받침 글자가 공백이 아니고 = 받침 글자가 존재하고
br[j+2] != 21 받침 글자가 21번째 글자 = ㅇ이 아니고
br[j+3]==11 다음 글자의 윗 자음이 ㅇ이고
br[j+1]!=77 "ㄱ" "ㅏ"와 같이 자음이나 모음만 입력한 경우가 아닌경우
바로 연음법칙이 적용조건이다.
"앉아"는 "안자"가 되야하니까
물로 이건 러브라떼 변환기라 이는 "안자"는 "야ㄴ쟈"가 될것이다.
if(br[j+2]==5){ br[j+2]=4; br[j+3]=12; } else if(br[j+2]==18){ br[j+2]=17; br[j+3]=9; }
앉아는 안자로
없어는 업서로 변동시킨다.
겹받침들중 ㄵ이랑 ㅄ은 자주 쓰는 발음이라 특별히 연음법칙을 적용시켰다.
다른 겹받침들은
일일히 적용하기 귀찮아서위에서 단자음들이랑 통합시켰었다.else{ if(br[j+2]==1) br[j+3]=0; else if(br[j+2]==4) br[j+3]=2; else if(br[j+2]==7) br[j+3]=3; else if(br[j+2]==8) br[j+3]=5; else if(br[j+2]==16) br[j+3]=6; else if(br[j+2]==17) br[j+3]=7; else if(br[j+2]==19) br[j+3]=9; else if(br[j+2]==22) br[j+3]=12; else if(br[j+2]==23) br[j+3]=14; else if(br[j+2]==24) br[j+3]=15; else if(br[j+2]==25) br[j+3]=16; else if(br[j+2]==26) br[j+3]=17; else if(br[j+2]==27) br[j+3]=18; else if(br[j+2]==2) br[j+3]=1; else if(br[j+2]==20) br[j+3]=10; br[j+2]=0; }
그 밑에,
단자음들의 연음법칙이다.
단자음이라 하는게 맞나 모르겠네
"박아"는 "바가"로
그리고 "뱌갸"로바꿔준다"깎아"는 까까로 쌍자음도 처리해준다.
이때 앞 글자의 받침이 없어지므로 br[j+2]=0을 마지막에 공통으로 적용한다.
위의 겯받침 연음법칙 사례들은 ex)"안자" "업서"
연음법칙을 적용해도 받침이 없어지지 않으므로
이를 적용하지 않아야하고
그러기 위해 따로 else문으로 묶은것이다.
if(br[j+1]==0 || br[j+1]==1 || br[j+1]==5) br[j+1]+=2; if(br[j+1]==13) br[j+1]=17; if(br[j+1]==11) br[j+1]=7; if(br[j+1]==16) br[j+1]=19; if(br[j+1]==12) br[j+1]=2; if(br[j+1]==14) br[j+1]=4; if(br[j+1]==4 && br[j+2]!=0) br[j+1]=6;
br[j+1]은 글자의 모음을 나타낸다.
즉 위 코드는 모음을 수정하는 코드이다.
ㅏ는 ㅑ로 ㅓ는 ㅕ로 ㅜ 는 ㅠ로 수정한다.
ㅗ는 수정하지 않는다.
그 편이 자연스럽고, 러브라떼 어법에 맞다(?)
그 외에는 규칙이랄게 딱히 없어서
위 글을
롤인벤 오빠언냐들아 반가워요 롤인벤 최강 귀요미가 롤인벤 자게 오빠언냐들한테도 사랑을 전하러 왔어요 일단은 그 시작으로 오빠와 저의 알콩달콩 사랑 이야기를 들려드릴게요
정상적으로 쓴 다음에(...)
이를 베이스로 위 문장에 맞춰나간다.
어차피 일상에서 쓰는 어휘가 거기서 거기고
결과물이 좀 이상해도 티가 안나기 때문에
그냥 무대뽀로 고치다보면 그럴듯해진다.
귀요미는 긔요미가 됐으므로 ㅟ를 ㅢ로
최강이 쳬강이 됐으므로 ㅚ를 ㅖ로
이런 식으로 쬐금씩 쬐금식 라떼어스럽게(?) 바꿔준다
로린볜 오뺘연냐듀랴야 뱐갸어요 로린볜 쳬걍 긔야미갸 로린볜 쟈계 오뺘연냐듀랴햔톄듀 샤량을 전하러 와써요 일댜는 그 시쟈그로 오뺘와 저으 얄콩댤콩 샤량 이야기를 듀랴려드릴계요
일단 모음만 바꿨더니 전혀 그 맛이 안산다
뭐부터 고쳐야할까 분석(?)해봤더니
그렇다. 글 중간중간 자음에 섞여있는게 크다.
자 그럼 이걸 이제 어떻게 구현하냐
if(br[j+2]==8){ br[j+2]=0; for(k=(br.length)-1 ; k>j+2 ; k--){ br[k+3]=br[k]; } br[j+3]=77; br[j+4]=77; br[j+5]=-31431; index+=3; }
br[j+2]은 8일때, 받침 글자가 ㄹ일때
"을"를 "으ㄹ"으로 바꿔야 한다.
br[j+2]="0";
일단 받침글자를 없애서 "을"은 "으"로 바뀌었고
이제 배열 중간에 3칸을 추가해서 "ㄹ"을 구현해야한다.
아까 말했듯 해당 배열은 값이 뭐가 됐든 배열을 3칸씩 읽기로 했기에
무조건 3칸 추가해야한다.
자 그럼 이제 배열의 5번 칸의 값을 8번칸으로, 6 칸의 값을 9번 칸으로...
이렇게 배열의 모든 값을 뒤로 3칸씩 미루면 3칸의 빈 공간이 사이에 생기게 되고
해당 공간에 "ㄹ" 값 77 77 8을 넣는다.
이것도 아까 말했듯 "ㄹ"같은 자음만 있는건 앞에 두칸을 77 77로 채워 구분한다.
그리고 여기서 문제.
5번 칸의 값을 8번 칸에 넣고, 6번 칸의 값을 9번 칸에 넣고..
그러면 빈자리는 생겼다만 배열 값은 멀쩡할까?
답은 전혀 아니다
5번 칸의 값이 8번 칸으로 옮겨졌기에,
8번 칸의 값이 11번 칸으로 갈때
8번 칸의 값은 이미 5번 칸의 값으로 바뀌었으므로
5번칸 값 - 6번칸 값 - 7번칸 값 - 5번칸 값 -6번칸 값...
이렇게 같은 3개의 값만 반복되어 기존의 정보가 전부 날라간다!
그렇기 위와같이 배열에 공간을 낼떄는 뒤에부터 옮겨야한다.
10번 칸의 값을 13번에 넣고
9번 칸의 값을 12번에 넣고
8번 칸의 값을 11번에 넣고
7번 칸의 값을 10번에 넣고...
이와같이 배열을 뒤부터 순차적으로 접근하는 방법역시
똑같이 for문을 사용한다.
for(k=(br.length)-1 ; k>j+2 ; k--){ br[k+3]=br[k];
기준이 되는 k의 값은 (br.length)-1
br은 몇번이나 말한 ㅂ ㅞ ㄺ 값이 저장되어있는 배열이고
br.length는 배열의 길이.
그리고 처음에 말했듯 배열은 0부터 시작하므로
길이가 27 = 총 27칸인 배열의 마지막 칸 번호는 br[26]이다.
그러니까 1 빼준다.
그리고 이번엔 반대로 배열에 마지막에서 점차 숫자가 줄어들어야하므로
k++이 아닌 k--를 해준다. --는 1 뺀다는 뜻이다.
혹시 까먹었을까 말하자면
우리는 "을"을 "으ㄹ"으로 바꾸는 코드를 짜고 있었다.
"ㄹ"을 넣을 공간을 만들기 위해 그 뒤에 있는 값들을 전부 3칸씩 뒤로 밀었고
그 3칸에 "ㄹ"의 정보 77 77 8을 넣는다.
br[j+3]=77; br[j+4]=77; br[j+5]=-31431;
j가 현재 글자 윗자음
j+1이 현재 글자 모음
j+2가 현재 글자 받침
j+3이 다음 글자 윗자음 = 77이 들어갈 공간
j+4가 다음 글자 모음 = 77이 들어갈 공간
J+5가 다음 글자 받침 = "ㄹ"값이 들어갈 공간
ㄹ값이 8이 아니라 -31431인데 편의상 그냥 8이라고 설명해왔다.
그냥 계산하려고 숫자 맞춘거니까 무시해도 된다.
쨌든 [j+3]부터 [j+5]에 새로운 값을 추가했으니.
배열의 끝부터 [j+3]까지의 모든 값을 뒤로 3칸씩 미뤄야한다.
for(k=(br.length)-1 ; k>j+2 ; k--){ br[k+3]=br[k];
즉 위 for문에서 기준 변수인 k가 j+3이 될때까지 반복해야하고,
그렇기에 조건은 k>j+2이다.
j+3이 된 다음에 해당 값을 옮긴 뒤 끝내야하니까.
index+=3;
그리고 배열이 3칸 늘었으니
배열의 길이를 나타내는 index 변수도 까먹지 말고 3칸 늘려준다.
로린볜 오뺘연냐듀랴야 뱐갸어얏 로린볜 쳬걍 긔야미갸 로린볜 쟈계 오뺘연냐듀랴햔톄듀 샤랴ㅇ으ㄹ 져냐러 오ㅏ써얏 이ㄹ댜는 그 시쟈그로 오뺘오ㅏ 저으 야ㄹ콩댜ㄹ콩 샤랴ㅇ 이야기르ㄹ 듀랴려드리ㄹ계야
제법 그럴싸해졌는데
중간에 ♥️같은 기호가 없으니까
라떼어라기보단 고전 구문같다(...)
중간중간 ♥️를 추가해주는 코드를 추가하자.
근데 띄어쓰기마다 추가하는것도 별로니
일단 ♥️는 띄어쓰기를 했을떄, 10% 확률로 추가하기로 했다.
Math.floor(Math.random() * 10)>8
Math.random()은 0부터 0.9999999~사이의 랜덤한 값을 리턴한다.
여기에 10을 곱했으니 0부터 9.999999~ 사이고
여기에 소숫점 밑을 없애는 Math.floor을 추가로 썼으니
0부터 9 사이의 값중 하나가 임의로 나오게된다.
그리고 해당 숫자가 8보다 클떄 = 9일떄 는
0부터 9, 총 10가지 경우의 수중 하나이므로 확률은 10%이다.
보통 코딩에서 랜덤 확률은 이와같이
랜덤한 수를 뽑아 정수로 만들어서
그 값이 ~ 이상일때를 조건으로 사용해서 구현한다.
if(Math.floor(Math.random() * 10)>8){ if(br[j]=="s" && br[j-3]!="h"){ for(k=(br.length)-1 ; k>j-1 ; k--){ br[k+3]=br[k]; } br[j]="h"; br[j+1]="h"; br[j+2]="h"; index+=3; } }
다만 br은 아직 자모음이 나뉘어있는 상태이다.
즉 지금 바로 하트를 추가해봤자 무의미하다.
"h"는 이후 해당 글자를 하트로 취급하란 의미로,
위에서 엔터를 n, 스페이스를 s로 표시해놨듯이 하트를 h로 표시해놨다.
그리고 스페이스 공백이 하트+공백, 즉 2글자가 된것이므로
을->으ㄹ처럼 배열을 뒤로 3칸씩 밀어서 그 사이에 h h h 값을 추가해준다.
즉 "시공 조아"는
"ㅅ ㅣ ㄱ ㅗ ㅇ h h h s s s ㅈ ㅗ ㅇ ㅏ"가 되고
라떼어로 번역해
"ㅅ ㅣ ㄱ ㅗ ㅇ h h h s s s ㅈ ㅗ ㅇ ㅑ"가 되고
이를 3개씩 합쳐
ㅅ ㅣ => 시
ㄱ ㅗ ㅇ => 공
h h h => ♥️
s s s => (공백)
ㅈ ㅗ => 조
ㅇ ㅑ => 야
"시공♥️ 조야"가 된다.
이제 위 "ㄱ ㅗ ㅇ -> 공"의 문자열 합치기를 구현해야한다.
for (j=0 ; j<index ; j+=3){
아까와 똑같이 배열을 3칸씩 읽는다.
아까는 배열을 수정하려고 3칸씩 읽은거고
이번엔 배열을 3칸씩 읽어 문자로 변경할꺼다.
if(br[j]=="n"){ hc[hi]="<"; hc[hi+1]="b"; hc[hi+2]="r"; hc[hi+3]=">"; hi+=4 }
n, 엔터일때는 새로운 배열 hc에 순서대 "<" "b" "r" ">"을 추가한다.
<br>은 html에서 엔터를 나타내는 블록이다.
아까 엔터는 \n으로 나타낸다 했는데
문자열에선 \n, html에선 <br>로 이라 보면 된다.
<br> <textarea id="before" cols="100" rows="10"></textarea> <br> <button onclick ='love()' >변환</button>
초반에 설명한 html 코드를 보면 여기에도 <br>이 들어가있는걸 알수있다.
else if(br[j]=="s"){ hc[hi]=" "; hi+=1 } else if(br[j]=="e"){ hc[hi]=el[ei2]; hi+=1 ei2+=1; } else if(br[j]=="h"){ hc[hi]="♥️"; hi+=1 }
그 외에 아까 설명한대로 s면 스페이스 h면 하트, e면 그 외에 것들 (else)
다만 e는 사용법이 살짝 다른데
el[]이라는 배열을 추가로 사용한다.
문자의 자모음을 분리할때 스페이스를 s라 지정했고
자음 모음을 합칠때 s가 나오면 이를 스페이스로 바꿨지만,
모든 단어를 그렇게 하나씩 지정해서 수정할 수는 없다.
그렇기에 그 외에 단어는 전부 e로 저장해서,
그 문자가 뭐였는지를 el 배열에 저장한다.
"강zk"를 바꾼다 치면
강은 ㄱ ㅏ ㅇ으로 바뀌었고,
z는 e e e로 바뀌고 el 배열에 z를 추가한다.
k도 e e e로 바뀌고 el 배열에 k를 추가한다.
즉 강zk는 ㄱ ㅏ ㅇ e e e e e e가 됐고
다시 합칠때 ㄱ ㅏ ㅇ 은 "강"이
처음 e를 만났을때 el 배열의 저장되어있는 첫값 "z"를 꺼내고 e 3개를 없앤뒤
두번쨰 e를 만났을때 el 배열의 두번째로 저장되어있는 값 "k"를 꺼내면
다시 "강zk"가 됨을 알수있다.
그러면 스페이스랑 엔터도 그냥 이렇게 처리하면 되는거 아니냐 할수있는데
엔터는 < b r > 문자 4개를 리턴해야되서 그냥 따로 처리하는게 편하고
스페이스는 ♥️ 넣는데 사용해서 따로 지정해야했다.
if(br[br.length-1]!="h" || br[br.length-1]!="s" || br[br.length-1]!="n"){ br[br.length]=888; index+=1; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ else if(br[j]=="888"){ hc[hi]="~"; hc[hi+1]="!"; hc[hi+2]=">"; hc[hi+3]="<"; hc[hi+4]="♥️"; }
그리고 설명 안한 코드가 있었는데
문자열의 마지막이 h,s,n이 아닌경우, 즉 문자인 경우 ~!><♥️ 를 마지막에 넣어준다.
else{ if(br[j]==77 && br[j+1]==77){ c[hi]= String.fromCharCode( br[j+2]+44032 ) } else{ hc[hi]= String.fromCharCode( br[j]*588 + br[j+1]*28 + br[j+2]+44032 ) } hi+=1; }
거의 다왔다
br[j]랑 br[j+1]이 77일떄, 즉 "ㅅ"과 같은 모음이나 자음만 있는 경우다
이때는 j+2값에 44032를 더했다.
편의상 8이다~라고 설명했지만 -33232 이런 값을 지닌 이유는 이떄문이다.
ㅅ을 숫자로 변환한 값이 12324고 여기에 44032를 뺸 값이 -33232, 지금 다시 더해서 원래 값 12324를 찾는다.
그러면 그냥 원래값 12324 쓰면 되는거 아니냐 할수있는데
어 생각해보니 그렇네 왜 그랬지
지금이야 코드를 순차적으로 설명하지만
코드 짤떄는 여기 구현했다 저기 구현했다 해서 정신이 없었나보다(...)
어차피 별 중요한건 아니니 패스그리고 위에 12324 -33232 이런거 귀찮아서 막 쓴 숫자니 진짜 계산해보지 마셈 ㅎ
var final = hc.join('');
정말 거의 다왔다.
이렇게 만들어진 문자 배열을 문자열로 바꿔준다.
그냥 출력하면 시,공,조,아 이렇게 뜨니 문자열로 합쳐 시공조아가 출력되게 한다
join 메서드로 쉽게 합칠수있다.
final = final.replace(/와/gi, "오ㅏ"); final = final.replace(/의/gi, "으"); final = final.replace(/야</gi, "얏~!☆<"); final = final.replace(/량/gi, "랴ㅇ"); final = final.replace(/을/gi, "유ㄹ"); final = final.replace(/를/gi, "류ㄹ");
그 다음엔 최종적으로,
"와"를 "오ㅏ"로 수정하고
"를"를 "류ㄹ"로 수정하고
좀 보정을 해준다(...)
document.write(final);
마지막으로 보정이 끝난 문자열을 출력
document.write는 printf 비슷한거다.
그냥 값 출력하는거니 설명은 패스
document 설명하려면 한세월걸린다.
롤인벤 오빠언냐들아 반가워요 롤인벤 최강 귀요미가 롤인벤 자게 오빠언냐들한테도 사랑을 전하러 왔어요 일단은 그 시작으로 오빠와 저의 알콩달콩 사랑 이야기를 들려드릴게요
로린볜 오뺘연냐듀랴야 뱐갸어얏~!☆ 로린볜 쳬걍 긔야미갸 로린볜 쟈계 오뺘연냐듀랴햔톄듀 샤랴ㅇ으ㄹ 져냐러♥️ 오ㅏ써얏~!☆ 이ㄹ댜는 그 시쟈그로 오뺘오ㅏ 저으 야ㄹ콩댜ㄹ콩 샤랴ㅇ 이야기르ㄹ 듀랴려드리ㄹ계야~!><♥️
그렇게 라뗴어 번역기 구현 완료!
https://gillyongs.github.io/github.io/lovelatte/
러브라뗴 변환기
gillyongs.github.io
깃헙IO 링크
https://github.com/gillyongs/github.io/tree/main/lovelatte
GitHub - gillyongs/github.io
Contribute to gillyongs/github.io development by creating an account on GitHub.
github.com
깃허브 링크
'토이 프로젝트' 카테고리의 다른 글
네모네모 멈뭄미 변환기 사이트 만들기 (0) 2022.03.23 포켓몬 빵 시뮬레이터 만들기 (0) 2022.03.23 듀자이크 생성기 만들기 (0) 2022.03.21 짤방 생성기 만들기 (0) 2022.03.21 러브라떼 변환기 업데이트 (1) 2022.03.21