Pro Yearly is on sale from $80 to $50! »

Remake of Hangulize

Remake of Hangulize

https://hangulize.org/

〈한글라이즈〉는 외국어 단어를 외래어 표기법에 따라 한글로 옮겨 적어주는 도구입니다. "Espresso"를 "에스프레소"로, "東京"을 "도쿄"로 변환할 수 있죠. 본래 Python으로 구현했던 한글라이즈를 Go로 재구현하면서 겪은 경험과 느낀점을 공유합니다.

고랭코리아 2018년 8월 밋업에서 발표했습니다.

C55dd7c0a68acd97963da84a3175cc66?s=128

Heungsub Lee

August 27, 2018
Tweet

Transcript

  1. 이흥섭 2018년 8월 고랭코리아 밋업 재제작기

  2. 이흥섭 2018년 8월 고랭코리아 밋업 재제작기 안녕하세요.

  3. 이흥섭 2018년 8월 고랭코리아 밋업 재제작기 2018년 8월 고랭코리아 밋업에서

    〈한글라이즈 재제작기〉를 발표하는
  4. 이흥섭 sublee 이흥섭이라고 합니다. 반갑습니다.

  5. •야생의 땅: 듀랑고 •카트라이더 코인러시 •카트라이더 대시 넥슨 왓 스튜디오

    서버아키텍트 저는 넥슨 왓 스튜디오에서 서버아키텍트를 맡고 있고
  6. •야생의 땅: 듀랑고 •카트라이더 코인러시 •카트라이더 대시 넥슨 왓 스튜디오

    서버아키텍트 얼마 전 〈야생의 땅: 듀랑고〉를 출시했습니다.
  7. •what-studio/profiling •sublee/trueskill •what-studio/tossi •hangulize/hangulize 오픈소스 2744 385 110 53 개수는

    2018년 9월 6일 기준
  8. •what-studio/profiling •sublee/trueskill •what-studio/tossi •hangulize/hangulize 오픈소스 2744 385 110 53 개수는

    2018년 9월 6일 기준 한편으론 틈틈이 오픈소스 활동도 하고 있어요.
  9. •what-studio/profiling •sublee/trueskill •what-studio/tossi •hangulize/hangulize 오픈소스 2744 385 110 53 개수는

    2018년 9월 6일 기준 제가 주축이 된 오픈소스 프로젝트 중에 별이 많은 것만 한 번 추려봤는데
  10. •what-studio/profiling •sublee/trueskill •what-studio/tossi •hangulize/hangulize 오픈소스 2744 385 110 53 개수는

    2018년 9월 6일 기준 이 중에선 가장 별이 적은 〈한글라이즈〉가 오늘 발표의 주제입니다.
  11. Go 코딩 진지한 장난감 협업 장난감 옹알이 실무 리뷰 구루

  12. Go 코딩 진지한 장난감 협업 장난감 옹알이 실무 리뷰 구루

    우리가 새로운 언어를 습득해 나갈 때 아마 이런 과정을 거치게 될 것 같은데
  13. Go 코딩 협업 장난감 옹알이 실무 리뷰 구루 고랭코리아 진지한

    장난감 Go 언어의 경우 고랭코리아 여러분은 대부분 이쯤에 계실 것 같습니다.
  14. Go 코딩 진지한 장난감 협업 장난감 옹알이 실무 리뷰 구루

    저 반면 저는 여기 쯤에 있어요.
  15. Go 코딩 진지한 장난감 협업 장난감 옹알이 실무 리뷰 구루

    저 Go 코딩은 매일 하고 있지만 아직 본격적으로 실무에 써보진 못 했습니다.
  16. Go 코딩 진지한 장난감 협업 장난감 옹알이 실무 리뷰 구루

    저 한글라이즈도 제 본업과는 전혀 무관하게
  17. 지난 3개월 일과 후 취미코딩 지난 3개월동안 매진했던 일과 후

    취미코딩이었어요.
  18. 지난 3개월 일과 후 취미코딩 그러다보니 아마 이 발표에서 제가

    여러분에게
  19. 지난 3개월 일과 후 취미코딩 Go에 대한 새로운 지식을 전해

    드리진 못 할 것 같은데
  20. 지난 3개월 일과 후 취미코딩 그래도 취미로 익히면서 재밌었던 점을

    소개해보려고 합니다.
  21. 지난 3개월 일과 후 취미코딩 학예회 보듯이 재밌게 봐주시면 감사하겠습니다.

  22. 1. 한글라이즈 2. Go 3. 이룬 것 차례

  23. 1. 한글라이즈 2. Go 3. 이룬 것 차례 우선 오늘

    발표의 내용은 이렇습니다.
  24. 1. 한글라이즈 2. Go 3. 이룬 것 차례 제가 만들고

    있는 한글라이즈에 대해 소개하고
  25. 1. 한글라이즈 2. Go 3. 이룬 것 차례 한글라이즈를 Go로

    만들면서 느낀 흥미로웠던 점에 대해서 말씀드릴게요.
  26. 1. 한글라이즈 2. Go 3. 이룬 것 차례 그리고 발표

    제목에 "재제작기"라고 적었듯이
  27. 1. 한글라이즈 2. Go 3. 이룬 것 차례 8년 전에

    파이썬으로 만들었던 한글라이즈를 이번에 Go로 다시 구현한 것이거든요.
  28. 1. 한글라이즈 2. Go 3. 이룬 것 차례 예전과 비교해서

    어떤 성과를 이뤘는지 보여드리겠습니다.
  29. 1. 한글라이즈 그럼 지금부터 한글라이즈가 무엇이고 이 프로젝트가 어떻게 시작됐는지

  30. 1. 한글라이즈 그리고 어떤 원리로 돌아가는지 소개해드릴게요.

  31. 외국어 단어를 한글로 옮겨 적는 도구 한글라이즈

  32. 외국어 단어를 한글로 옮겨 적는 도구 한글라이즈 한글라이즈는 외국어 단어를

    한글로 옮겨 적어주는 도구예요.
  33. 말소리를 음성 문자로 옮겨 적음.다음사전 의미를 옮기는 번역(translation)과는 달라요. 전사(transcription)

    좀 더 정확히는 "전사"라고 부르는데
  34. 말소리를 음성 문자로 옮겨 적음.다음사전 의미를 옮기는 번역(translation)과는 달라요. 전사(transcription)

    다음사전에서 찾아보면 말소리를 음성 문자로 옮겨 적는 걸 뜻한다고 합니다.
  35. 말소리를 음성 문자로 옮겨 적음.다음사전 의미를 옮기는 번역(translation)과는 달라요. 전사(transcription)

    한글라이즈의 역할은 외국어 단어의 의미를 해석해서
  36. 말소리를 음성 문자로 옮겨 적음.다음사전 의미를 옮기는 번역(translation)과는 달라요. 전사(transcription)

    우리말로 번역하는 게 아니라 발음만 한글로 표기하는 것이죠.
  37. Go▶ 고(전사) ▶ 가(번역) 영어 단어 "Go"를 예로 들면

  38. Go▶ 고(전사) ▶ 가(번역) 발음 그대로 "고"로 옮기는 건 전사고

  39. Go▶ 고(전사) ▶ 가(번역) 의미를 살려 우리말 "가"로 옮기는 건

    번역입니다.
  40. PlayOverwatch Lúcio ▶ 루시우 花村 ▶ 하나무라 인명 지명 Overwatch

    Wiki
  41. PlayOverwatch Lúcio ▶ 루시우 花村 ▶ 하나무라 인명 지명 Overwatch

    Wiki 전사는 인명이나 지명같은 고유명사나 외래어를 우리말로 옮길 때 필요합니다.
  42. PlayOverwatch Lúcio ▶ 루시우 花村 ▶ 하나무라 인명 지명 Overwatch

    Wiki 지명인 "하나무라"를 굳이 "꽃 마을"이라고 뜻을 번역해서 옮기지는 않겠죠.
  43. 국립국어원이 정한 외래어 네이밍 컨벤션 외래어 표기법 이렇게 외래어를 한글로

    전사할 때 우리는
  44. 국립국어원이 정한 외래어 네이밍 컨벤션 외래어 표기법 귀에 들리는 대로

    주먹구구식으로 옮기는 대신
  45. 국립국어원이 정한 외래어 네이밍 컨벤션 외래어 표기법 국립국어원에서 정한 "외래어

    표기법"을 따라야 합니다.
  46. 국립국어원이 정한 외래어 네이밍 컨벤션 외래어 표기법 코딩할 때 따르는

    네이밍 컨벤션과 비슷하다고 보면 될 것 같아요.
  47. 국립국어원이 정한 외래어 네이밍 컨벤션 외래어 표기법 모두가 외래어 표기법을

    잘 따르는 건 아니지만
  48. 국립국어원이 정한 외래어 네이밍 컨벤션 외래어 표기법 그래도 이게 있는

    덕분에 외래어의 한글표기가 일관성을 어느정도 지켜갈 수 있는 거죠.
  49. Cappuccino이탈리아어 ▶ 몇 가지 예시를 들어볼게요.

  50. Cappuccino이탈리아어 ▶ 익숙한 이탈리아어 단어인 "Cappuccino"입니다.

  51. Cappuccino이탈리아어 ▶ 캡푸씨노…? 어쩌면 "캡푸씨노"라고 옮겨도 말이 될 것 같은데

  52. Cappuccino이탈리아어 ▶ 카푸치노 우린 외래어 표기법에 따라서 이 단어를 "카푸치노"라고

    옮깁니다.
  53. Cappuccino이탈리아어 ▶ 카푸치노 "티라미수", "누텔라", "브로콜리"같이 이탈리아어에서 온 외래어는

  54. Cappuccino이탈리아어 ▶ 카푸치노 대체로 외래어 표기법을 잘 따르는 것 같습니다.

  55. 宮本茂일본어 ▶ 이번엔 일본어입니다. 일본 사람 이름이에요.

  56. 宮本茂일본어 ▶ 궁본무…? 아마 한자를 잘 안다면 우리말 한자음을 따서

    "궁본무"라고 옮길 수도 있을 것 같아요.
  57. 宮本茂일본어 ▶ 미야모토 시게루 이 이름을 표기법대로 옮기면 "미야모토 시게루"가

    되는데
  58. 宮本茂일본어 ▶ 미야모토 시게루 우리말 한자음이 아닌 일본어 한자음을 따른

    겁니다.
  59. 宮本茂일본어 ▶ 미야모토 시게루 〈슈퍼마리오〉를 만든 분으로 게임 업계에서 전설적인

    분의 이름이죠.
  60. Χίος그리스어 ▶ 이건 그리스에 있는 한 섬의 이름이에요. "Xioc"처럼 생기긴

    했는데
  61. Χίος그리스어 ▶ 히오스 "히오스"라고 옮깁니다.

  62. ピカチュウ일본어 ▶ 피카츄 때론 표기법대로 쓰는 게 불편할 수도 있어요.

    일본어 이름인 "피카츄"의 경우
  63. ピカチュウ일본어 ▶ 피카추 외래어 표기법에 따르면 "츄"가 아닌 "추"로 표기해야

    합니다.
  64. • ピカチュウ ▶ 피카츄 •靑島 ▶ 칭따오 • Chevrolet ▶

    쉐보레 어디까지나 권고사항 피카추 칭다오 셰브럴레이 하지만 외래어 표기법은 어디까지나 권고사항일 뿐이라서
  65. • ピカチュウ ▶ 피카츄 •靑島 ▶ 칭따오 • Chevrolet ▶

    쉐보레 어디까지나 권고사항 피카추 칭다오 셰브럴레이 이미 다른 표기가 널리 퍼져 있는 브랜드라면 국립국어원에서도 그쪽의 손을 들어주고 있습니다.
  66. 제1항 외래어는 국어의 현용 24 자모만으로 적는다. 제2항 외래어의 1

    음운은 원칙적으로 1 기호로 적는다. 제3항 받침에는 ㄱ, ㄴ, ㄹ, ㅁ, ㅂ, ㅅ, ㅇ만을 쓴다. 제4항 파열음 표기에는 된소리를 쓰지 않는 것을 원칙으로 한다. 제5항 이미 굳어진 외래어는 관용을 존중하되, 그 범위와 용례는 따로 정한다. 외래어 표기법 자음 반모음 모음 국제 음성 기호 한글 국제 음성 기호 한글 국제 음성 기호 한글 모음 앞 자음 앞 또는 어 말 p ㅍ ㅂ, 프 j 이 i 이 b ㅂ 브 ɥ 위 y 위 t ㅌ ㅅ, 트 w 오, 우 e 에 d ㄷ 드 φ 외 k ㅋ ㄱ, 크 ɛ 에 g ㄱ 그 ɛ̃ 앵 f ㅍ 프 œ 외 v ㅂ 브 œ̃ 욍 θ ㅅ 스 æ 애 ð ㄷ 드 a 아 s ㅅ 스 ɑ 아
  67. 제1항 외래어는 국어의 현용 24 자모만으로 적는다. 제2항 외래어의 1

    음운은 원칙적으로 1 기호로 적는다. 제3항 받침에는 ㄱ, ㄴ, ㄹ, ㅁ, ㅂ, ㅅ, ㅇ만을 쓴다. 제4항 파열음 표기에는 된소리를 쓰지 않는 것을 원칙으로 한다. 제5항 이미 굳어진 외래어는 관용을 존중하되, 그 범위와 용례는 따로 정한다. 외래어 표기법 자음 반모음 모음 국제 음성 기호 한글 국제 음성 기호 한글 국제 음성 기호 한글 모음 앞 자음 앞 또는 어 말 p ㅍ ㅂ, 프 j 이 i 이 b ㅂ 브 ɥ 위 y 위 t ㅌ ㅅ, 트 w 오, 우 e 에 d ㄷ 드 φ 외 k ㅋ ㄱ, 크 ɛ 에 g ㄱ 그 ɛ̃ 앵 f ㅍ 프 œ 외 v ㅂ 브 œ̃ 욍 θ ㅅ 스 æ 애 ð ㄷ 드 a 아 s ㅅ 스 ɑ 아 외래어 표기법은 이런 규칙집으로 이뤄져 있습니다.
  68. 제1항 외래어는 국어의 현용 24 자모만으로 적는다. 제2항 외래어의 1

    음운은 원칙적으로 1 기호로 적는다. 제3항 받침에는 ㄱ, ㄴ, ㄹ, ㅁ, ㅂ, ㅅ, ㅇ만을 쓴다. 제4항 파열음 표기에는 된소리를 쓰지 않는 것을 원칙으로 한다. 제5항 이미 굳어진 외래어는 관용을 존중하되, 그 범위와 용례는 따로 정한다. 외래어 표기법 자음 반모음 모음 국제 음성 기호 한글 국제 음성 기호 한글 국제 음성 기호 한글 모음 앞 자음 앞 또는 어 말 p ㅍ ㅂ, 프 j 이 i 이 b ㅂ 브 ɥ 위 y 위 t ㅌ ㅅ, 트 w 오, 우 e 에 d ㄷ 드 φ 외 k ㅋ ㄱ, 크 ɛ 에 g ㄱ 그 ɛ̃ 앵 f ㅍ 프 œ 외 v ㅂ 브 œ̃ 욍 θ ㅅ 스 æ 애 ð ㄷ 드 a 아 s ㅅ 스 ɑ 아 국립국어원 홈페이지에서 누구나 자유롭게 볼 수 있죠.
  69. 제1항 외래어는 국어의 현용 24 자모만으로 적는다. 제2항 외래어의 1

    음운은 원칙적으로 1 기호로 적는다. 제3항 받침에는 ㄱ, ㄴ, ㄹ, ㅁ, ㅂ, ㅅ, ㅇ만을 쓴다. 제4항 파열음 표기에는 된소리를 쓰지 않는 것을 원칙으로 한다. 제5항 이미 굳어진 외래어는 관용을 존중하되, 그 범위와 용례는 따로 정한다. 외래어 표기법 자음 반모음 모음 국제 음성 기호 한글 국제 음성 기호 한글 국제 음성 기호 한글 모음 앞 자음 앞 또는 어 말 p ㅍ ㅂ, 프 j 이 i 이 b ㅂ 브 ɥ 위 y 위 t ㅌ ㅅ, 트 w 오, 우 e 에 d ㄷ 드 φ 외 k ㅋ ㄱ, 크 ɛ 에 g ㄱ 그 ɛ̃ 앵 f ㅍ 프 œ 외 v ㅂ 브 œ̃ 욍 θ ㅅ 스 æ 애 ð ㄷ 드 a 아 s ㅅ 스 ɑ 아 하지만 워낙 복잡해서 아무리 잘 따르려 노력해도 틀리는 경우가 많습니다.
  70. •정기적으로 각 외래어의 한글 표기 결정 • 언론에 보도되는 이름

    중심 정부·언론 외래어 심의 공동위원회 그래서 바른 말 쓰기를 특히 중요하게 여기는 정부와 언론 쪽에선
  71. •정기적으로 각 외래어의 한글 표기 결정 • 언론에 보도되는 이름

    중심 정부·언론 외래어 심의 공동위원회 외래어 표기법이 잘 지켜지고 있는지 평가하고
  72. •정기적으로 각 외래어의 한글 표기 결정 • 언론에 보도되는 이름

    중심 정부·언론 외래어 심의 공동위원회 정기적으로 공식 외래어 표기를 발표하는 위원회를 두고 있어요.
  73. •정기적으로 각 외래어의 한글 표기 결정 • 언론에 보도되는 이름

    중심 정부·언론 외래어 심의 공동위원회 "정부·언론 외래어 심의 공동위원회"라는 곳인데 코드리뷰어 집단이라고 생각하면 될 것 같습니다.
  74. 제92차 정부·언론외래어심의공동위원회 심의 결정안 (인명 52건, 일반용어 1건-재심의 2건) •

    아베라, 암살라 Amsale Aberra 1954(?1953)~ 에티오피아 여성 기업가·디자이너. 암살라(Amsale) 그룹 대표 겸 디자 인 총책임자. 유행을 좇지 않으면서 세련된 클래식 모던을 강조하는 디자인으로 유명함. • 아벨란제, 주앙 João Havelange 1916~ 국제축구연맹(FIFA) 회장(1974~1998). 브라질 인. • 그루벨, 루스 Ruth M. Grubel 1950~ 미국 교육가·선교사. 일본 간사이(關西)학원 원장(2007. 4.~ ).
  75. 제92차 정부·언론외래어심의공동위원회 심의 결정안 (인명 52건, 일반용어 1건-재심의 2건) •

    아베라, 암살라 Amsale Aberra 1954(?1953)~ 에티오피아 여성 기업가·디자이너. 암살라(Amsale) 그룹 대표 겸 디자 인 총책임자. 유행을 좇지 않으면서 세련된 클래식 모던을 강조하는 디자인으로 유명함. • 아벨란제, 주앙 João Havelange 1916~ 국제축구연맹(FIFA) 회장(1974~1998). 브라질 인. • 그루벨, 루스 Ruth M. Grubel 1950~ 미국 교육가·선교사. 일본 간사이(關西)학원 원장(2007. 4.~ ). 이건 외래어 심의위가 발표한 표기 심의 결정안 중 하나인데
  76. 제92차 정부·언론외래어심의공동위원회 심의 결정안 (인명 52건, 일반용어 1건-재심의 2건) •

    아베라, 암살라 Amsale Aberra 1954(?1953)~ 에티오피아 여성 기업가·디자이너. 암살라(Amsale) 그룹 대표 겸 디자 인 총책임자. 유행을 좇지 않으면서 세련된 클래식 모던을 강조하는 디자인으로 유명함. • 아벨란제, 주앙 João Havelange 1916~ 국제축구연맹(FIFA) 회장(1974~1998). 브라질 인. • 그루벨, 루스 Ruth M. Grubel 1950~ 미국 교육가·선교사. 일본 간사이(關西)학원 원장(2007. 4.~ ). 아버라, 암살러 그루블 심의위에서도 드물게 틀리는 경우가 있습니다.
  77. 제92차 정부·언론외래어심의공동위원회 심의 결정안 (인명 52건, 일반용어 1건-재심의 2건) •

    아베라, 암살라 Amsale Aberra 1954(?1953)~ 에티오피아 여성 기업가·디자이너. 암살라(Amsale) 그룹 대표 겸 디자 인 총책임자. 유행을 좇지 않으면서 세련된 클래식 모던을 강조하는 디자인으로 유명함. • 아벨란제, 주앙 João Havelange 1916~ 국제축구연맹(FIFA) 회장(1974~1998). 브라질 인. • 그루벨, 루스 Ruth M. Grubel 1950~ 미국 교육가·선교사. 일본 간사이(關西)학원 원장(2007. 4.~ ). 아버라, 암살러 그루블 원 단어의 발음을 잘못 유추하는 바람에 표기를 잘못 정하는 경우가 종종 있다고 해요.
  78. •세계의 말과 글 •끝소리 박종성 @iceager

  79. •세계의 말과 글 •끝소리 박종성 @iceager 한편 예전부터 외래어 표기법에

    큰 관심을 보인 분이 있는데
  80. •세계의 말과 글 •끝소리 박종성 @iceager 바로 박종성 님입니다.

  81. •세계의 말과 글 •끝소리 박종성 @iceager 종성 님은 "세계의 말과

    글"이라는 블로그와 "끝소리"라는 페이스북 페이지에서
  82. •세계의 말과 글 •끝소리 박종성 @iceager 외래어 표기법이나 여러가지 언어에

    대한 글을 연재하고 계세요.
  83. •세계의 말과 글 •끝소리 박종성 @iceager 연재하는 글이 굉장히 깊고

    재밌으니
  84. •세계의 말과 글 •끝소리 박종성 @iceager 혹시 이 발표가 흥미롭다면

    한 번 들어가 보길 추천해드립니다.
  85. •세계의 말과 글 •끝소리 외래어 심의 위원 박종성 @iceager 종성

    님은 작년부터는 글 연재 이외에
  86. •세계의 말과 글 •끝소리 외래어 심의 위원 박종성 @iceager 외래어

    심의위 위원으로도 활동하고 계십니다.
  87. 정기적으로 회의를 열어 용례를 정하는 것으 로는 한계가 있다. 외래어

    표기 심의 방식이 자동화되어 한글로 표기하고 싶은 외국어를 입력하자마자 한글 표기가 나와야 한다. http://iceager.egloos.com/2610028
  88. 정기적으로 회의를 열어 용례를 정하는 것으 로는 한계가 있다. 외래어

    표기 심의 방식이 자동화되어 한글로 표기하고 싶은 외국어를 입력하자마자 한글 표기가 나와야 한다. http://iceager.egloos.com/2610028 8년 전, 종성 님은 블로그에 이런 글을 쓰셨어요.
  89. 정기적으로 회의를 열어 용례를 정하는 것으 로는 한계가 있다. 외래어

    표기 심의 방식이 자동화되어 한글로 표기하고 싶은 외국어를 입력하자마자 한글 표기가 나와야 한다. http://iceager.egloos.com/2610028 정기적으로 회의를 열어 용례를 정하는 것으로는 한계가 있다.
  90. 정기적으로 회의를 열어 용례를 정하는 것으 로는 한계가 있다. 외래어

    표기 심의 방식이 자동화되어 한글로 표기하고 싶은 외국어를 입력하자마자 한글 표기가 나와야 한다. http://iceager.egloos.com/2610028 외래어 표기 심의 방식이 자동화되어
  91. 정기적으로 회의를 열어 용례를 정하는 것으 로는 한계가 있다. 외래어

    표기 심의 방식이 자동화되어 한글로 표기하고 싶은 외국어를 입력하자마자 한글 표기가 나와야 한다. http://iceager.egloos.com/2610028 한글로 표기하고 싶은 외국어를 입력하자마자 한글 표기가 나와야 한다.
  92. 강범모, 《언어: 풀어 쓴 언어학 개론》(2005) 잠깐 샛길로 빠지면, 제가

    대학교 다닐 때 제일 좋아했던 강의는 〈언어학 개론〉이었거든요.
  93. 강범모, 《언어: 풀어 쓴 언어학 개론》(2005) 언어학은 사람이 구사하는 언어를

    연구하는 과학분야인데
  94. 강범모, 《언어: 풀어 쓴 언어학 개론》(2005) 학생 절반이 수능시험의 언어영역

    생각하고 들어왔다가
  95. 강범모, 《언어: 풀어 쓴 언어학 개론》(2005) 첫 강의만 듣고 대거

    취소했던 강의이긴 하지만 저에겐 인생 강의였습니다.
  96. 강범모, 《언어: 풀어 쓴 언어학 개론》(2005) 언어학을 전공한 건 아니더라도

    늘 덕심을 갖고 있었죠.
  97. 정기적으로 회의를 열어 용례를 정하는 것으 로는 한계가 있다. 외래어

    표기 심의 방식이 자동화되어 한글로 표기하고 싶은 외국어를 입력하자마자 한글 표기가 나와야 한다. http://iceager.egloos.com/2610028 그런 전 종성 님의 이 글을 보고 깊은 감명을 받았어요.
  98. 정기적으로 회의를 열어 용례를 정하는 것으 로는 한계가 있다. 외래어

    표기 심의 방식이 자동화되어 한글로 표기하고 싶은 외국어를 입력하자마자 한글 표기가 나와야 한다. http://iceager.egloos.com/2610028 외래어 표기법이 자동화되면 당장 저에게도 도움될 것 같았고
  99. 정기적으로 회의를 열어 용례를 정하는 것으 로는 한계가 있다. 외래어

    표기 심의 방식이 자동화되어 한글로 표기하고 싶은 외국어를 입력하자마자 한글 표기가 나와야 한다. http://iceager.egloos.com/2610028 그걸 만드는 과정도 굉장히 재밌어 보였죠.
  100. 그래서 덥석 물었습니다.

  101. 아쉽게도 비공개 덧글이고 암호도 잊어서 뭐라고 썼었는진 모르겠지만

  102. 그렇게 종성 님과 둘이서 외래어 표기법을 자동화하기 시작했어요.

  103. 파이썬

  104. 파이썬 당시엔 한창 파이썬을 익히는 중이어서 파이썬으로 개발했고

  105. >>> from hangulize import * >>> hangulize(u'Cappuccino', 'ita') u'카푸치노' 옛

    한글라이즈 이런 식으로 파이썬 환경에서 쓸 수 있는 "hangulize" 라이브러리를 만들었습니다.
  106. >>> from hangulize import * >>> hangulize(u'Cappuccino', 'ita') u'카푸치노' 옛

    한글라이즈 이 파이썬 구현을 "옛 한글라이즈"라고 부를게요.
  107. https://hangulize.org/ 꼭 프로그래머가 아니더라도 누구나 쉽게 쓸 수 있도록

  108. https://hangulize.org/ "hangulize.org" 웹 서비스도 운영해왔는데

  109. 주로 번역가가 사용 이지민, 《그래도 번역가로 살겠다면》(2017) 이 웹 서비스가

    번역가 분들에겐 소소한 도움을 주고 있는 것 같더라고요.
  110. •주먹구구식 구현 •고치기 어려운 버그 •전사 규칙이 파이썬 코드 옛

    한글라이즈 불만 하지만 제가 어렸을 때 만든 것이다 보니
  111. •주먹구구식 구현 •고치기 어려운 버그 •전사 규칙이 파이썬 코드 옛

    한글라이즈 불만 지금 보면 맘에 들지 않는 부분도 몇 가지 있는데
  112. •주먹구구식 구현 •고치기 어려운 버그 •전사 규칙이 파이썬 코드 옛

    한글라이즈 불만 주먹구구식 구현으로 고치기 어려운 버그를 만든 것도 문제지만
  113. •주먹구구식 구현 •고치기 어려운 버그 •전사 규칙이 파이썬 코드 옛

    한글라이즈 불만 특히 외래어 표기법 전사 규칙을 파이썬 코드로 쓰게 했던 점이 가장 후회됩니다.
  114. •주먹구구식 구현 •고치기 어려운 버그 •전사 규칙이 파이썬 코드 옛

    한글라이즈 불만 프로그래머가 아니면 진입장벽도 높고 버그를 야기할 여지도 많아서
  115. •주먹구구식 구현 •고치기 어려운 버그 •전사 규칙이 파이썬 코드 옛

    한글라이즈 불만 새 표기법을 구현할 때 생산성이 그리 좋지 않았던 것 같아요.
  116. 옛 한글라이즈 2010-2018 이런 후회를 뒤로 하고 옛 한글라이즈는 얼마

    전에 공식적으로 폐쇄했습니다.
  117. Go Go로 완전히 새로 구현했죠.

  118. gore> :import "github.com/hangulize/hangulize" gore> hangulize.Hangulize("rus", "Владивосто́ к") "블라디보스토크" 새 한글라이즈

    2018년 5월-
  119. gore> :import "github.com/hangulize/hangulize" gore> hangulize.Hangulize("rus", "Владивосто́ к") "블라디보스토크" 새 한글라이즈

    2018년 5월- "새 한글라이즈"는 8년 만인 지난 5월부터 만들었어요.
  120. gore> :import "github.com/hangulize/hangulize" gore> hangulize.Hangulize("rus", "Владивосто́ к") "블라디보스토크" 새 한글라이즈

    2018년 5월- 옛 한글라이즈처럼 Go 환경에서 쓸 수 있는 간단한 라이브러리죠.
  121. lang: id = "rus" codes = "ru", "rus" english =

    "Russian" korean = "러시아어" script = "cyrillic" macros: "@" = "<vowels>" vars: "cs" = "б", "в", "г" 전사 규칙 .hgl 파일 rewrite: "град" -> "град-" "^ль" -> "л-ь" "ый" -> "ы" transcribe: "б" -> "ㅂ" "л," -> "-ㄹ" test: "Ольга" -> "올가" "Пётр" -> "표트르" 새로 구현하면서는 전사 규칙도 코드 대신 이런 텍스트파일로 정의할 수 있게 설계했는데
  122. lang: id = "rus" codes = "ru", "rus" english =

    "Russian" korean = "러시아어" script = "cyrillic" macros: "@" = "<vowels>" vars: "cs" = "б", "в", "г" 전사 규칙 .hgl 파일 rewrite: "град" -> "град-" "^ль" -> "л-ь" "ый" -> "ы" transcribe: "б" -> "ㅂ" "л," -> "-ㄹ" test: "Ольга" -> "올가" "Пётр" -> "표트르" 정해진 틀 안에서 쓰다 보니 버그가 발생할 여지도 적고
  123. lang: id = "rus" codes = "ru", "rus" english =

    "Russian" korean = "러시아어" script = "cyrillic" macros: "@" = "<vowels>" vars: "cs" = "б", "в", "г" 전사 규칙 .hgl 파일 rewrite: "град" -> "град-" "^ль" -> "л-ь" "ый" -> "ы" transcribe: "б" -> "ㅂ" "л," -> "-ㄹ" test: "Ольга" -> "올가" "Пётр" -> "표트르" 맞춤형 문법이라서 읽기도 훨씬 편해졌습니다.
  124. H 형상, 로마자 i, 화살표, 한글ㅣ 옛 로고 새 로고

    그리고 8년 만에 새 로고도 디자인했죠.
  125. 四川 쓰촨

  126. 四川 쓰촨 이번엔 한글라이즈의 원리를 살펴보기 위해 중국 지명인 "쓰촨"을

    예로 들어보겠습니다.
  127. Rewrite Transcribe Phonemize 四川 쓰촨 한글라이즈 파이프라인 한글라이즈에 한자 "四川"이

    입력되면
  128. Rewrite Transcribe Phonemize 四川 쓰촨 한글라이즈 파이프라인 일련의 파이프라인을 통과해

    최종적으로 "쓰촨"이 되죠.
  129. •뜻글자를 소리글자로 •사전 •형태소 분석 1단계: Phonemize Rewrite Transcribe Phonemize

    四川 si chuan 파이프라인의 첫 단계는 Phonemize 단계인데
  130. •뜻글자를 소리글자로 •사전 •형태소 분석 1단계: Phonemize Rewrite Transcribe Phonemize

    四川 si chuan 여기선 뜻글자를 소리글자로 바꾸는 작업을 합니다.
  131. •뜻글자를 소리글자로 •사전 •형태소 분석 1단계: Phonemize Rewrite Transcribe Phonemize

    四川 si chuan 한자 "四川"의 경우 중국어 병음 "si chuan"으로 바뀌죠.
  132. •뜻글자를 소리글자로 •사전 •형태소 분석 1단계: Phonemize Rewrite Transcribe Phonemize

    四川 si chuan Phonemize 단계가 모든 언어에서 필요한 건 아니지만
  133. •뜻글자를 소리글자로 •사전 •형태소 분석 1단계: Phonemize Rewrite Transcribe Phonemize

    四川 si chuan 중국어나 일본어처럼 뜻글자를 쓰는 경우엔 이 단계를 거쳐야만 한글로 옮길 수 있습니다.
  134. •뜻글자를 소리글자로 •사전 •형태소 분석 1단계: Phonemize Rewrite Transcribe Phonemize

    四川 si chuan 이때 방대한 발음 사전이나 형태소 분석 같은 기능이 필요한데
  135. •뜻글자를 소리글자로 •사전 •형태소 분석 1단계: Phonemize Rewrite Transcribe Phonemize

    四川 si chuan 여기에 어떤 라이브러리를 썼는지는 뒤에서 다시 소개하겠습니다.
  136. •패턴 찾아 바꾸기 •표음성 강화 2단계: Rewrite Rewrite Transcribe Phonemize

    si chuan "{s}i" -> "i," si, chuan "{h}uan" -> "wan" si, chwan "n$" -> "n," si, chwan,
  137. •패턴 찾아 바꾸기 •표음성 강화 2단계: Rewrite Rewrite Transcribe Phonemize

    si chuan "{s}i" -> "i," si, chuan "{h}uan" -> "wan" si, chwan "n$" -> "n," si, chwan, 두 번째 Rewrite 단계에선 일련의 패턴 찾아 바꾸기로
  138. •패턴 찾아 바꾸기 •표음성 강화 2단계: Rewrite Rewrite Transcribe Phonemize

    si chuan "{s}i" -> "i," si, chuan "{h}uan" -> "wan" si, chwan "n$" -> "n," si, chwan, 앞에서 넘어온 "si chuan"을 "si, chwan,"로 바꿔줍니다.
  139. •패턴 찾아 바꾸기 •표음성 강화 2단계: Rewrite Rewrite Transcribe Phonemize

    si chuan "{s}i" -> "i," si, chuan "{h}uan" -> "wan" si, chwan "n$" -> "n," si, chwan, 이 단계의 용도는 소리글자의 표음성을 강화하는 것인데
  140. 표음성 신도림행 열차 지옥행 급행열차 표음성 극대화 형태학적 표기 신도리맹

    녈차 지오캥 그팽녈차 여기서 "표음성"이란
  141. 표음성 신도림행 열차 지옥행 급행열차 표음성 극대화 형태학적 표기 신도리맹

    녈차 지오캥 그팽녈차 소리글자가 실제 소리에 대응되는 정도를 뜻합니다.
  142. 표음성 신도림행 열차 지옥행 급행열차 표음성 극대화 형태학적 표기 신도리맹

    녈차 지오캥 그팽녈차 한글도 왼쪽처럼 소리나는 대로만 적으면 표음성은 극대화되지만
  143. 표음성 신도림행 열차 지옥행 급행열차 표음성 극대화 형태학적 표기 신도리맹

    녈차 지오캥 그팽녈차 한 눈에 의미를 파악하긴 더 어렵죠.
  144. 표음성 신도림행 열차 지옥행 급행열차 표음성 극대화 형태학적 표기 신도리맹

    녈차 지오캥 그팽녈차 그래서 철자법에선 오른쪽처럼 표음성을 희생하고
  145. 표음성 신도림행 열차 지옥행 급행열차 표음성 극대화 형태학적 표기 신도리맹

    녈차 지오캥 그팽녈차 대신 단어의 형태와 어원을 살리는 경우가 많습니다.
  146. •패턴 찾아 바꾸기 •표음성 강화 2단계: Rewrite Rewrite Transcribe Phonemize

    si chuan "{s}i" -> "i," si, chuan "{h}uan" -> "wan" si, chwan "n$" -> "n," si, chwan, 따라서 소리글자라고 해도 실제 소리에 1:1 대응되는 경우는 별로 없는데
  147. •패턴 찾아 바꾸기 •표음성 강화 2단계: Rewrite Rewrite Transcribe Phonemize

    si chuan "{s}i" -> "i," si, chuan "{h}uan" -> "wan" si, chwan "n$" -> "n," si, chwan, 한글로 옮기려면 의미가 아닌 발음이 필요하니까
  148. •패턴 찾아 바꾸기 •표음성 강화 2단계: Rewrite Rewrite Transcribe Phonemize

    si chuan "{s}i" -> "i," si, chuan "{h}uan" -> "wan" si, chwan "n$" -> "n," si, chwan, 이 단계에서 철자로부터 발음을 최대한 유추합니다.
  149. •한글 자모로 변환 •패턴 찾아 바꾸기 •외래어 표기법 구현 3단계:

    Transcribe Rewrite Transcribe Phonemize si, chwan, "wan,?" -> "ㅘ-ㄴ" si, chㅘ-ㄴ "i," -> "ㅡ" sㅡ chㅘ-ㄴ "ch" -> "ㅊ" sㅡ ㅊㅘ-ㄴ "s" -> "ㅆ" ㅆㅡ ㅊㅘ-ㄴ
  150. •한글 자모로 변환 •패턴 찾아 바꾸기 •외래어 표기법 구현 3단계:

    Transcribe Rewrite Transcribe Phonemize si, chwan, "wan,?" -> "ㅘ-ㄴ" si, chㅘ-ㄴ "i," -> "ㅡ" sㅡ chㅘ-ㄴ "ch" -> "ㅊ" sㅡ ㅊㅘ-ㄴ "s" -> "ㅆ" ㅆㅡ ㅊㅘ-ㄴ 그렇게 구한 발음을 세 번째 Transcribe 단계에선
  151. •한글 자모로 변환 •패턴 찾아 바꾸기 •외래어 표기법 구현 3단계:

    Transcribe Rewrite Transcribe Phonemize si, chwan, "wan,?" -> "ㅘ-ㄴ" si, chㅘ-ㄴ "i," -> "ㅡ" sㅡ chㅘ-ㄴ "ch" -> "ㅊ" sㅡ ㅊㅘ-ㄴ "s" -> "ㅆ" ㅆㅡ ㅊㅘ-ㄴ 대응되는 한글 자모로 바꿔줍니다.
  152. •한글 자모로 변환 •패턴 찾아 바꾸기 •외래어 표기법 구현 3단계:

    Transcribe Rewrite Transcribe Phonemize si, chwan, "wan,?" -> "ㅘ-ㄴ" si, chㅘ-ㄴ "i," -> "ㅡ" sㅡ chㅘ-ㄴ "ch" -> "ㅊ" sㅡ ㅊㅘ-ㄴ "s" -> "ㅆ" ㅆㅡ ㅊㅘ-ㄴ 여기가 외래어 표기법이 실제로 구현되는 단계죠.
  153. •한글 자모로 변환 •패턴 찾아 바꾸기 •외래어 표기법 구현 3단계:

    Transcribe Rewrite Transcribe Phonemize si, chwan, "wan,?" -> "ㅘ-ㄴ" si, chㅘ-ㄴ "i," -> "ㅡ" sㅡ chㅘ-ㄴ "ch" -> "ㅊ" sㅡ ㅊㅘ-ㄴ "s" -> "ㅆ" ㅆㅡ ㅊㅘ-ㄴ Transcribe 단계도 Rewrite 단계와 마찬가지로 일련의 패턴 찾아 바꾸기로 이뤄져 있습니다.
  154. Rewrite Transcribe Phonemize 四川 쓰촨 한글라이즈 파이프라인 si chuan si,

    chwan, ㅆㅡㅊㅘ-ㄴ 마지막으로 나열된 자모들을 한글 글자로 모아쓰기하면 전사가 완료돼요.
  155. 아제르바이잔어 벨라루스어 불가리아어 카탈로니아어 체코어 중국어 웨일스어 독일어 그리스어 에스페란토어

    에스토니아어 핀란드어 고대 그리스어 세르보크로아트어 헝가리어 아이슬란드어 이탈리아어 일본어 일본어최영애-김용옥 조지아어제1안 조지아어제2안 라틴어 라트비아어 리투아니아어 마케도니아어 네덜란드어 폴란드어 포르투갈어 브라질 포르투갈어 루마니아어 러시아어 슬로바키아어 슬로베니아어 스페인어 알바니아어 스웨덴어 터키어 우크라이나어 베트남어 웨일스어중세
  156. 아제르바이잔어 벨라루스어 불가리아어 카탈로니아어 체코어 중국어 웨일스어 독일어 그리스어 에스페란토어

    에스토니아어 핀란드어 고대 그리스어 세르보크로아트어 헝가리어 아이슬란드어 이탈리아어 일본어 일본어최영애-김용옥 조지아어제1안 조지아어제2안 라틴어 라트비아어 리투아니아어 마케도니아어 네덜란드어 폴란드어 포르투갈어 브라질 포르투갈어 루마니아어 러시아어 슬로바키아어 슬로베니아어 스페인어 알바니아어 스웨덴어 터키어 우크라이나어 베트남어 웨일스어중세 한글라이즈엔 현재 일본어, 헝가리어 등 총 40개의 전사 규칙이 들어있어요.
  157. 영어 하지만 아쉽게도 가장 수요가 많은 영어는 아직 지원하지 못

    하고 있습니다.
  158. 영어 영어는 특히나 표음성이 낮은 언어라서 그런데

  159. • though ▶ • through ▶ • rough ▶ •

    cough ▶ • thought ▶ • bough ▶ 영어의 낮은 표음성 예를 들어 "ough"는 이렇게나 다양한 발음을 갖고 있지만
  160. • though ▶ • through ▶ • rough ▶ •

    cough ▶ • thought ▶ • bough ▶ 영어의 낮은 표음성 철자 규칙만으로 발음을 정확하게 유추하는 건 불가능하다고 합니다.
  161. Ghoti ▶ 영어의 낮은 표음성을 비꼬는 유명한 농담으로 이런 것도

    있습니다.
  162. Ghoti ▶ "고티"라고 읽을 것 같은 단어인데

  163. Ghoti ▶ enough women nation "gh"는 "enough"에서, "o"는 "women"에서, "ti"는

    "nation"에서 따와서
  164. Ghoti ▶ 피시 enough women nation "피시"라고 읽어야 한다고 합니다.

  165. 2. Go

  166. 2. Go 이번엔 한글라이즈를 Go로 다시 만들면서 저에게 흥미로웠던 점들을

    소개해 드릴게요.
  167. 초보 주의 진지한 장난감 협업 장난감 옹알이 실무 리뷰 구루

    저 고랭코리아 우선 들어가기 전에 앞에서 말씀드렸듯이 전 Go를 취미로만 써봤거든요.
  168. 초보 주의 진지한 장난감 협업 장난감 옹알이 실무 리뷰 구루

    저 고랭코리아 큰 코드베이스에서 남들과 협업해보거나 실무에서 제대로 써본 적이 아직 없습니다.
  169. 초보 주의 진지한 장난감 협업 장난감 옹알이 실무 리뷰 구루

    저 고랭코리아 그래서 너무 당연한 얘기를 하거나 틀린 얘기를 할 수도 있는데
  170. 초보 주의 진지한 장난감 협업 장난감 옹알이 실무 리뷰 구루

    저 고랭코리아 그럴 경우 부담 없이 sub@subl.ee로 제보해주시면 감사하겠습니다.
  171. 고루틴 끝내준다! Go 첫인상

  172. 고루틴 끝내준다! Go 첫인상 몇 년 전에 Go 튜토리얼 책을

    처음 보면서 들었던 첫인상은 이랬습니다.
  173. 고루틴 끝내준다! Go 첫인상 고루틴 끝내준다!

  174. 고루틴 끝내준다! Go 첫인상 이렇게 병렬 프로그래밍을 쉽게 할 수

    있는 환경은 처음 봤었거든요.
  175. 고루틴 끝내준다! Go 첫인상 쓸 때마다 늘 피곤했던 병렬처리 단위,

    채널, 큐 등을
  176. 고루틴 끝내준다! Go 첫인상 언어의 기본 기능으로 제공한다는 데에 깜짝

    놀랐죠.
  177. •고루틴은 끝내주는데 •그런데 너무 기능이 적다. Go 첫인상 그런데 그

    밖엔 취향에 맞지 않는 점이 많았어요.
  178. •고루틴은 끝내주는데 •그런데 너무 기능이 적다. Go 첫인상 그 흔한

    반복자나 재정의, 예외도 없고 특히 제네릭이 없어서
  179. •고루틴은 끝내주는데 •그런데 너무 기능이 적다. Go 첫인상 범용적인 정적

    자료구조를 만들 수 없다는 점도 의아하게 느껴졌습니다.
  180. •고루틴은 끝내주는데 •그런데 너무 기능이 적다. •고퍼도 귀엽지 않고 Go

    첫인상 그리고 무엇보다도 고퍼가 영 귀엽지 않았죠.
  181. 이걸로 정말 제품이 만들어질까? Go 첫인상 이렇게 단순한 언어로 정말

    강력한 제품을 만들 수 있을까 의문이 들었습니다.
  182. Docker etcd Terraform K8s 하지만 점점 더 많은 유명 프로젝트가

    Go로 만들어지고 있는 모습을 보면서
  183. 된다. 아주 잘 된다는 걸 깨닫게 됐죠.

  184. 협업 장난감 옹알이 실무 리뷰 구루 진지한 장난감 저도 한

    번 Go를 제대로 익혀보고 싶어서 간단한 취미코딩에 써보기 시작했어요.
  185. https://subl.ee/runker/ 예를 들면 이런 겁니다.

  186. https://subl.ee/runker/ 〈섭리랑카〉라는 간단한 〈똥피하기〉 패러디 게임인데

  187. https://subl.ee/runker/ 랭킹서버가 Go 여기서 랭킹서버가 Go와 구글 앱 엔진으로 돼있습니다.

  188. sublee/subpptx 지금 이 발표자료에 자막 붙이는 데 쓰고 있는 "subpptx"도

    Go로 만들었어요.
  189. sublee/subpptx 이 프로젝트에선 고루틴 덕분에 손쉽게 페이지 단위로 병렬처리할 수

    있었죠.
  190. 쓸 만 하다. 더 잘 하고 싶다. 이런저런 간단한 장난감을

    만들면서 Go를 써보니 꽤 쓸 만 하고 재미있다는 인상을 받았습니다.
  191. 쓸 만 하다. 더 잘 하고 싶다. Go 특유의 간결함이

    이제 명료함으로 느껴지더라고요.
  192. 쓸 만 하다. 더 잘 하고 싶다. 몇 십 줄

    짜리 간단한 장난감에서 벗어나 좀 더 진지한 걸 만들어보면서
  193. 쓸 만 하다. 더 잘 하고 싶다. Go를 더 잘

    다루고 싶다고 생각하게 됐습니다.
  194. 한글라이즈 다시 만들면 왠지 더 잘 만들 것 같다.

  195. 한글라이즈 다시 만들면 왠지 더 잘 만들 것 같다. 석

    달 전 마침
  196. 한글라이즈 다시 만들면 왠지 더 잘 만들 것 같다. 한글라이즈를

    다시 만들면 왠지 더 잘 만들 것 같다는 생각이 떠올랐어요.
  197. •주먹구구식 구현 •고치기 어려운 버그 •전사 규칙이 파이썬 코드 옛

    한글라이즈 불만 앞에서 말씀드렸듯이 기존 한글라이즈에서 맘에 들지 않는 구석도 많았고
  198. • 이미 한 번 만들어 봄 • 적당한 복잡성 •

    계산 중심적 •유니코드/정규표현식 •풍부한 테스트케이스 •언어학 덕질 연습과제: 한글라이즈 또 한글라이즈가 저에겐 새로운 언어를 익힐 때 연습과제로 삼기 꽤 좋은 프로젝트거든요.
  199. • 이미 한 번 만들어 봄 • 적당한 복잡성 •

    계산 중심적 •유니코드/정규표현식 •풍부한 테스트케이스 •언어학 덕질 연습과제: 한글라이즈 이미 한 번 만들어봐서 불확실성은 별로 없는데
  200. • 이미 한 번 만들어 봄 • 적당한 복잡성 •

    계산 중심적 •유니코드/정규표현식 •풍부한 테스트케이스 •언어학 덕질 연습과제: 한글라이즈 적당히 복잡하면서 상당히 계산 중심적이다 보니
  201. • 이미 한 번 만들어 봄 • 적당한 복잡성 •

    계산 중심적 •유니코드/정규표현식 •풍부한 테스트케이스 •언어학 덕질 연습과제: 한글라이즈 구조나 알고리즘에 대한 고민도 다시 한 번 해보게 되고
  202. • 이미 한 번 만들어 봄 • 적당한 복잡성 •

    계산 중심적 •유니코드/정규표현식 •풍부한 테스트케이스 •언어학 덕질 연습과제: 한글라이즈 그 언어가 유니코드와 정규표현식을 어떻게 다루는지 깊게 겪어볼 수도 있고
  203. • 이미 한 번 만들어 봄 • 적당한 복잡성 •

    계산 중심적 •유니코드/정규표현식 •풍부한 테스트케이스 •언어학 덕질 연습과제: 한글라이즈 또 이미 만들어둔 테스트케이스가 많아서 제대로 짰는지 검증할 재료도 충분하죠.
  204. • 이미 한 번 만들어 봄 • 적당한 복잡성 •

    계산 중심적 •유니코드/정규표현식 •풍부한 테스트케이스 •언어학 덕질 연습과제: 한글라이즈 마지막으로 제가 덕질할 수 있는 주제여서
  205. • 이미 한 번 만들어 봄 • 적당한 복잡성 •

    계산 중심적 •유니코드/정규표현식 •풍부한 테스트케이스 •언어학 덕질 연습과제: 한글라이즈 몇 달 정도는 지긋이 해볼 수 있는 과제이기도 합니다.
  206. • 이미 한 번 만들어 봄 • 적당한 복잡성 •

    계산 중심적 •유니코드/정규표현식 •풍부한 테스트케이스 •언어학 덕질 연습과제: 한글라이즈 그렇게 파이썬으로 만들었던 한글라이즈를 Go로 다시 만들게 됐습니다.
  207. a. 정규표현식 b. 말과 글 c. 테스트와 문서화

  208. a. 정규표현식 b. 말과 글 c. 테스트와 문서화 그럼 Go에서

    흥미로웠던 세 가지 주제로 넘어가 보겠습니다.
  209. a. 정규표현식 b. 말과 글 c. 테스트와 문서화 좀 두서

    없을 수도 있는데 정규표현식, 말과 글, 테스트와 문서화에 대해 다룰 거예요.
  210. a. 정규표현식

  211. a. 정규표현식 첫 번째는 정규표현식입니다. 줄여서 "정규식"이라고도 부르는데

  212. /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~- ]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d- \x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9- ]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0- 5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0- 9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e- \x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/ 이메일 정규표현식 http://emailregex.com/

    텍스트 매칭하는 데에 다들 한 번 쯤은 써보셨을 것 같아요.
  213. /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~- ]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d- \x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9- ]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0- 5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0- 9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e- \x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/ 이메일 정규표현식 http://emailregex.com/

    정규식 엔진엔 몇 가지 알고리즘이 있지만
  214. import "regexp" Go 표준 정규식 라이브러리는

  215. •안전한 정규표현식 구현 •문자열 길이에 대해 선형 시간 •고의적으로 일부

    기능 미지원 RE2 by Google 구글에서 만든 정규식 엔진인 RE2를 기반으로 하고 있습니다.
  216. •안전한 정규표현식 구현 •문자열 길이에 대해 선형 시간 •고의적으로 일부

    기능 미지원 RE2 by Google RE2는 평균적으로 가장 빠른 정규식 엔진은 아니더라도
  217. •안전한 정규표현식 구현 •문자열 길이에 대해 선형 시간 •고의적으로 일부

    기능 미지원 RE2 by Google 대상 문자열 길이에 정비례하는 선형 시간을 보장하는 걸 목표로 삼는다고 합니다.
  218. •안전한 정규표현식 구현 •문자열 길이에 대해 선형 시간 •고의적으로 일부

    기능 미지원 RE2 by Google 그래서 선형 시간에 구현할 수 없는 정규식의 일부 고급 기능은 고의적으로 지원하지 않죠.
  219. O(mn) RE2 성능 보장 RE2의 시간복잡도는 평균, 최악 모두 O(mn)입니다.

  220. O(mn) RE2 성능 보장 정규표현식 길이 대상 문자열 길이 여기서

    m은 정규식 패턴의 길이고 n은 대상 문자열의 길이예요.
  221. O(mn) RE2 성능 보장 정규표현식 길이 대상 문자열 길이 grep

    같이 정규식을 사용자입력으로 받는 프로그램이 아닌 이상
  222. O( n) RE2 성능 보장 대상 문자열 길이 보통 상수항

    정규식 패턴은 코드에 미리 적어 두는 경우가 대부분이다 보니
  223. O( n) RE2 성능 보장 대상 문자열 길이 보통 상수항

    사실상 m을 상수항으로 취급해도 좋습니다. 상당히 훌륭하죠.
  224. /a?a?a?aaa/를 "aaa"에 매치 벤치마크 Russ Cox, 〈Regular Expression Matching Can

    Be Simple And Fast〉(2007)
  225. /a?a?a?aaa/를 "aaa"에 매치 벤치마크 Russ Cox, 〈Regular Expression Matching Can

    Be Simple And Fast〉(2007) RE2를 만든 Russ Cox의 홈페이지에 가보면
  226. /a?a?a?aaa/를 "aaa"에 매치 벤치마크 Russ Cox, 〈Regular Expression Matching Can

    Be Simple And Fast〉(2007) 두 종류의 정규식 엔진을 비교하는 재미있는 벤치마크가 있습니다.
  227. /a?a?a?aaa/를 "aaa"에 매치 벤치마크 Russ Cox, 〈Regular Expression Matching Can

    Be Simple And Fast〉(2007) Russ Cox는 Go 개발진으로도 유명한 분이죠?
  228. /a?a?a?aaa/를 "aaa"에 매치 벤치마크 Russ Cox, 〈Regular Expression Matching Can

    Be Simple And Fast〉(2007) 우선 이 예시를 보시죠.
  229. /a?a?a?aaa/를 "aaa"에 매치 벤치마크 Russ Cox, 〈Regular Expression Matching Can

    Be Simple And Fast〉(2007) 패턴엔 /a?/와 리터럴 /a/가 각각 3개씩 있고
  230. /a?a?a?aaa/를 "aaa"에 매치 벤치마크 Russ Cox, 〈Regular Expression Matching Can

    Be Simple And Fast〉(2007) 대상 문자열은 "a" 3개로 이뤄져 있습니다.
  231. /a?a?a?aaa/를 "aaa"에 매치 벤치마크 Russ Cox, 〈Regular Expression Matching Can

    Be Simple And Fast〉(2007) 패턴에서 /a?/는 전부 건너뛰고 리터럴 /a/ 3개만 매치시켜야
  232. /a?a?a?aaa/를 "aaa"에 매치 벤치마크 Russ Cox, 〈Regular Expression Matching Can

    Be Simple And Fast〉(2007) 매치에 성공할 수 있는 패턴이에요.
  233. f(1) = /a?a/를 "a"에 매치 f(3) = /a?a?a?aaa/를 "aaa"에 매치

    f(5) = /a?a?a?a?a?aaaaa/를 "aaaaa"에 매치 f(x) = /a?ˣaˣ/를 "aˣ"에 매치 벤치마크 Russ Cox, 〈Regular Expression Matching Can Be Simple And Fast〉(2007) 벤치마크는 패턴과 문자열의 길이를 바꿔가며 진행할 텐데
  234. f(1) = /a?a/를 "a"에 매치 f(3) = /a?a?a?aaa/를 "aaa"에 매치

    f(5) = /a?a?a?a?a?aaaaa/를 "aaaaa"에 매치 f(x) = /a?ˣaˣ/를 "aˣ"에 매치 벤치마크 Russ Cox, 〈Regular Expression Matching Can Be Simple And Fast〉(2007) 이런 식으로 /a?/와 /a/를 x개만큼 반복하는 패턴과
  235. f(1) = /a?a/를 "a"에 매치 f(3) = /a?a?a?aaa/를 "aaa"에 매치

    f(5) = /a?a?a?a?a?aaaaa/를 "aaaaa"에 매치 f(x) = /a?ˣaˣ/를 "aˣ"에 매치 벤치마크 Russ Cox, 〈Regular Expression Matching Can Be Simple And Fast〉(2007) x개의 "a"로 이뤄진 대상 문자열을 가지고
  236. f(1) = /a?a/를 "a"에 매치 f(3) = /a?a?a?aaa/를 "aaa"에 매치

    f(5) = /a?a?a?a?a?aaaaa/를 "aaaaa"에 매치 f(x) = /a?ˣaˣ/를 "aˣ"에 매치 벤치마크 Russ Cox, 〈Regular Expression Matching Can Be Simple And Fast〉(2007) x를 늘려가면서 속도를 비교해볼 겁니다.
  237. f(1) = /a?a/를 "a"에 매치 f(3) = /a?a?a?aaa/를 "aaa"에 매치

    f(5) = /a?a?a?a?a?aaaaa/를 "aaaaa"에 매치 f(x) = /a?ˣaˣ/를 "aˣ"에 매치 벤치마크 Russ Cox, 〈Regular Expression Matching Can Be Simple And Fast〉(2007) 저는 파이썬의 정규식 표준 라이브러리 "re"와
  238. f(1) = /a?a/를 "a"에 매치 f(3) = /a?a?a?aaa/를 "aaa"에 매치

    f(5) = /a?a?a?a?a?aaaaa/를 "aaaaa"에 매치 f(x) = /a?ˣaˣ/를 "aˣ"에 매치 벤치마크 Russ Cox, 〈Regular Expression Matching Can Be Simple And Fast〉(2007) Go의 정규식 표준 라이브러리 "regexp" 두 가지로 직접 돌려봤어요.
  239. /a?ˣaˣ/를 "aˣ"에 매치 시간 파이썬의 re Go의 regexp f(x) 결과는

    이랬습니다.
  240. /a?ˣaˣ/를 "aˣ"에 매치 시간 파이썬의 re Go의 regexp f(x) 파이썬

    쪽 곡선이 압도적으로 가파르게 올라갔죠.
  241. /a?ˣaˣ/를 "aˣ"에 매치 시간 파이썬의 re Go의 regexp f(x) Russ

    Cox의 설명에 의하면
  242. /a?ˣaˣ/를 "aˣ"에 매치 시간 f(x) O(x²) O(2ˣ) 시간복잡도가 Go 쪽은

    O(x²)이고 파이썬 쪽은 O(2ˣ)이라고 합니다.
  243. /a?ˣaˣ/를 "aˣ"에 매치 시간 f(x) O(x²) O(2ˣ) 언뜻 보면 제곱

    시간도 나쁜 성능처럼 보이지만
  244. /a?ˣaˣ/를 "aˣ"에 매치 시간 f(x) O(x²) O(2ˣ) 이 벤치마크 함수는

    x가 커질 수록 패턴 길이 m과 문자열 길이 n을 같이 키우니까
  245. /a?ˣaˣ/를 "aˣ"에 매치 시간 f(x) O(x²) O(2ˣ) 제곱 시간이면 사실은

    이상적인 성능이라고 볼 수 있습니다.
  246. /a?ˣaˣ/를 "aˣ"에 매치 파이썬 re Go regexp f(10) 39.7㎲ 1.8㎲

    f(15) 1.3㎳ 3.8㎲ f(20) 47.2㎳ 6.6㎲ f(25) 1.7초 9.7㎲ f(30) 61.8초 13.9㎲ f(50) 676일(예상) 38.5㎲ 제가 돌려본 벤치마크 환경에선 x가 50일 때 Go 쪽은 38.5㎲면 끝나지만
  247. /a?ˣaˣ/를 "aˣ"에 매치 파이썬 re Go regexp f(10) 39.7㎲ 1.8㎲

    f(15) 1.3㎳ 3.8㎲ f(20) 47.2㎳ 6.6㎲ f(25) 1.7초 9.7㎲ f(30) 61.8초 13.9㎲ f(50) 676일(예상) 38.5㎲ 파이썬 쪽은 1년 9개월이나 걸릴 것으로 예상되더라고요.
  248. Spencer 류 펄, PCRE, 파이썬, 루비, 자바 Thompson 류 awk,

    sed, grep, RE2, Go, 러스트
  249. Spencer 류 펄, PCRE, 파이썬, 루비, 자바 Thompson 류 awk,

    sed, grep, RE2, Go, 러스트 앞서 정규식 엔진엔 몇 가지 알고리즘이 있다고 말씀드렸는데
  250. Spencer 류 펄, PCRE, 파이썬, 루비, 자바 Thompson 류 awk,

    sed, grep, RE2, Go, 러스트 크게 Spencer 류와 Thompson 류로 나눌 수 있을 것 같습니다.
  251. Spencer 류 펄, PCRE, 파이썬, 루비, 자바 Thompson 류 awk,

    sed, grep, RE2, Go, 러스트 파이썬 re는 Spencer 류에 속하고 RE2와 Go regexp는 Thompson 류에 속하죠.
  252. Spencer 류 펄, PCRE, 파이썬, 루비, 자바 Thompson 류 awk,

    sed, grep, RE2, Go, 러스트 앞에서 돌렸던 벤치마크의 경우 Spencer 류 엔진에선 지수 시간이 걸리지만
  253. Spencer 류 펄, PCRE, 파이썬, 루비, 자바 Thompson 류 awk,

    sed, grep, RE2, Go, 러스트 Thompson 류 엔진에선 제곱 시간밖에 걸리지 않습니다.
  254. Spencer 류 펄, PCRE, 파이썬, 루비, 자바 Thompson 류 awk,

    sed, grep, RE2, Go, 러스트 왜 이런 차이가 생기는지 x=3일 때를 예로 들어서 살펴볼게요.
  255. Spencer 류 "aaa" a? a? a? aaa a?가 있다고 가정

    a?가 없다고 가정
  256. Spencer 류 "aaa" a? a? a? aaa a?가 있다고 가정

    a?가 없다고 가정 Spencer 류 엔진은 /a?/라는 분기를 만나면
  257. Spencer 류 "aaa" a? a? a? aaa a?가 있다고 가정

    a?가 없다고 가정 우선 /a?/가 존재한다고 가정하고 다음으로 넘어갑니다.
  258. Spencer 류 "aaa" a? a? a? aaa a?가 있다고 가정

    a?가 없다고 가정 /a?a?a?/가 전부 있다고 가정하고 끝까지 가보면
  259. Spencer 류 "aaa" a? a? a? aaa a?가 있다고 가정

    a?가 없다고 가정 대상 문자열의 끝에 도달해서 리터럴 /a/를 매치시킬 곳이 없단 걸 깨닫죠.
  260. Spencer 류 "aaa" a? a? a? aaa aaa a?가 있다고

    가정 a?가 없다고 가정 그럼 마지막 분기로 되돌아가서 그 /a?/가 없다고 가정합니다.
  261. Spencer 류 "aaa" a? a? a? aaa aaa a?가 있다고

    가정 a?가 없다고 가정 하지만 이 길 역시 틀린 길이죠.
  262. Spencer 류 "aaa" a? a? a? aaa a? aaa aaa

    aaa a?가 있다고 가정 a?가 없다고 가정 한 단계 더 물러나서 이번엔 두 번째 /a?/가 없다고 가정해요.
  263. Spencer 류 "aaa" a? a? a? aaa a? aaa aaa

    aaa a?가 있다고 가정 a?가 없다고 가정 그 상태에서 앞에서와 같이 세 번째 분기도 다시 탐색합니다.
  264. Spencer 류 "aaa" a? a? a? aaa a? aaa aaa

    aaa a?가 있다고 가정 a?가 없다고 가정 새로운 두 가지 길이 있지만 이번에도 모두 틀린 길이죠.
  265. Spencer 류 "aaa" a? a? a? aaa a? a? aaa

    a? aaa a? aaa aaa aaa aaa a?가 있다고 가정 a?가 없다고 가정 aaa 이젠 첫 번째 /a?/도 없다고 가정하고 그동안 탐색했던 길만큼 다시 탐색합니다.
  266. Spencer 류 "aaa" a? a? a? aaa a? a? aaa

    a? aaa a? aaa aaa aaa aaa a?가 있다고 가정 a?가 없다고 가정 aaa 매치! 마지막 길까지 살펴보면 마침내 패턴이 매치된단 걸 알 수 있습니다.
  267. a? a? a? aaa a? a? aaa a? aaa a?

    aaa aaa aaa aaa aaa 퇴각검색 (backtracking) 이런 식으로 일단 길 하나를 쭉 파보고 틀렸으면 퇴각한 다음
  268. a? a? a? aaa a? a? aaa a? aaa a?

    aaa aaa aaa aaa aaa 퇴각검색 (backtracking) 다른 길로 다시 파보는 방식을 "퇴각검색"이라고 하는데
  269. a? a? a? aaa a? a? aaa a? aaa a?

    aaa aaa aaa aaa aaa 퇴각검색 (backtracking) Spencer 류 정규식 엔진은 분기를 퇴각검색으로 구현합니다.
  270. Thompson 류 "aaa" a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a

    반면 Thompson 류 정규식 엔진은 대상 문자열의 각 글자에 대해서
  271. Thompson 류 "aaa" a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a

    가능한 선택지를 모두 평가하면서 전진해요.
  272. Thompson 류 "aaa" a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a

    대상 문자열의 첫 번째 "a"의 경우 패턴의 세 /a?/ 중 어디에도 매치될 수 있고
  273. Thompson 류 "aaa" a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a

    첫 번째 리터럴 /a/에도 매치될 수 있어서 총 4가지 가능성을 가집니다.
  274. Thompson 류 "aaa" a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa

    a?a?a?aaa 첫 번째 a 두 번째 a 그 다음 앞쪽 가능성이 참일 경우에 뒤따르게 될 다음 가능성을 두 번째 "a"에 매치해보고
  275. Thompson 류 "aaa" a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa

    a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a 두 번째 a 세 번째 a 세 번째 "a"에서도 같은 일을 반복합니다.
  276. Thompson 류 "aaa" a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa

    a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a 두 번째 a 세 번째 a 매치! 그러면 패턴 끝까지 도달한 가능성이 하나 나타나는데
  277. Thompson 류 "aaa" a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa

    a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a 두 번째 a 세 번째 a 매치! 바로 매치를 찾은 것이죠.
  278. Thompson 류 "aaa" a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa

    a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a 두 번째 a 세 번째 a 매치! 퇴각검색 방식을 쓰는 Spencer 류에서와 달리
  279. Thompson 류 "aaa" a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa

    a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a 두 번째 a 세 번째 a 매치! Thompson 류에서 대상 문자열의 각 글자는 단 한 번씩만 읽히는데
  280. Thompson 류 "aaa" a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa

    a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a 두 번째 a 세 번째 a 매치! 즉, 되돌아가는 일 없이 전진만 하는 것입니다.
  281. Spencer 류 Thompson 류 "aaa" "aaa" a? a? a? aaa

    a? a? aaa a? aaa a? aaa aaa aaa aaa aaa 2ˣ a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a 두 번째 a 세 번째 a x+1
  282. Spencer 류 Thompson 류 "aaa" "aaa" a? a? a? aaa

    a? a? aaa a? aaa a? aaa aaa aaa aaa aaa 2ˣ a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a 두 번째 a 세 번째 a x+1 이 벤치마크에서 Spencer 류가 탐색하는 가능성은 2ˣ개인데 반해
  283. Spencer 류 Thompson 류 "aaa" "aaa" a? a? a? aaa

    a? a? aaa a? aaa a? aaa aaa aaa aaa aaa 2ˣ a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a 두 번째 a 세 번째 a x+1 Thompson 류는 x+1개만 탐색합니다.
  284. Spencer 류 Thompson 류 "aaa" "aaa" a? a? a? aaa

    a? a? aaa a? aaa a? aaa aaa aaa aaa aaa 2ˣ a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a 두 번째 a 세 번째 a x+1 그래서 Spencer 류의 탐색 횟수는 지수적으로 증가하고
  285. Spencer 류 Thompson 류 "aaa" "aaa" a? a? a? aaa

    a? a? aaa a? aaa a? aaa aaa aaa aaa aaa 2ˣ a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a 두 번째 a 세 번째 a x+1 Thompson 류의 탐색 횟수는 선형적으로 증가하죠.
  286. Spencer 류 Thompson 류 "aaa" "aaa" a? a? a? aaa

    a? a? aaa a? aaa a? aaa aaa aaa aaa aaa 2ˣ a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa a?a?a?aaa 첫 번째 a 두 번째 a 세 번째 a x+1 이 때문에 특정한 경우에 둘 사이에서 극단적인 성능차이를 볼 수 있습니다.
  287. •평균적으론 충분히 빠르고 •표현력이 더 좋아요. •펄, PCRE, 파이썬, 루비,

    자바 Spencer 류
  288. •평균적으론 충분히 빠르고 •표현력이 더 좋아요. •펄, PCRE, 파이썬, 루비,

    자바 Spencer 류 하지만 일반적인 경우 Spencer 류 정규식 엔진도 충분히 빠르게 동작해요.
  289. •평균적으론 충분히 빠르고 •표현력이 더 좋아요. •펄, PCRE, 파이썬, 루비,

    자바 Spencer 류 패턴만 안전하게 짜면 Thompson 류만큼 안정적으로 쓸 수 있죠.
  290. •평균적으론 충분히 빠르고 •표현력이 더 좋아요. •펄, PCRE, 파이썬, 루비,

    자바 Spencer 류 그리고 정규식 기능 중엔 퇴각검색으로만 구현할 수 있는 것도 있어서
  291. •평균적으론 충분히 빠르고 •표현력이 더 좋아요. •펄, PCRE, 파이썬, 루비,

    자바 Spencer 류 이쪽의 정규식 표현력이 훨씬 좋습니다.
  292. •평균적으론 충분히 빠르고 •표현력이 더 좋아요. •펄, PCRE, 파이썬, 루비,

    자바 Spencer 류 그래서인지 펄과 PCRE, 파이썬을 비롯해
  293. •평균적으론 충분히 빠르고 •표현력이 더 좋아요. •펄, PCRE, 파이썬, 루비,

    자바 Spencer 류 오늘날 수많은 정규식 엔진이 이 방식을 쓴다고 해요.
  294. /(a+)+/ /([a-zA-Z]+)*/ /(a|aa)+/ /(a|a?)+/ ReDoS 공격 하지만 Spencer 류 정규식

    엔진을 쓰는 서버가
  295. /(a+)+/ /([a-zA-Z]+)*/ /(a|aa)+/ /(a|a?)+/ ReDoS 공격 만약 사용자 입력으로 정규식

    패턴을 받는다면 얘기가 달라집니다.
  296. /(a+)+/ /([a-zA-Z]+)*/ /(a|aa)+/ /(a|a?)+/ ReDoS 공격 공격자가 정규식 엔진에서 최악의

    경우를 이끌어내는
  297. /(a+)+/ /([a-zA-Z]+)*/ /(a|aa)+/ /(a|a?)+/ ReDoS 공격 악의적인 정규식 패턴으로 ReDoS

    공격을 가할 수 있거든요.
  298. /(a+)+/ /([a-zA-Z]+)*/ /(a|aa)+/ /(a|a?)+/ ReDoS 공격 구글에서도 ReDoS 공격에 대한

    우려가 있어서
  299. /(a+)+/ /([a-zA-Z]+)*/ /(a|aa)+/ /(a|a?)+/ ReDoS 공격 Thompson의 정규식 알고리즘을 기반으로

    RE2를 만들게 됐다고 합니다.
  300. "^gli{@}" "^^(a|e|i|o|u)" "{~@}sub<cons>" HRE 한글라이즈 정규표현식 방언

  301. "^gli{@}" "^^(a|e|i|o|u)" "{~@}sub<cons>" HRE 한글라이즈 정규표현식 방언 한편 한글라이즈에선 패턴

    찾아 바꾸기 할 때
  302. "^gli{@}" "^^(a|e|i|o|u)" "{~@}sub<cons>" HRE 한글라이즈 정규표현식 방언 "HRE"로 명명한 한글라이즈

    전용 정규표현식 방언을 쓰고 있어요.
  303. "^gli{@}" "^^(a|e|i|o|u)" "{~@}sub<cons>" HRE 한글라이즈 정규표현식 방언 파란색으로 강조한 부분은

    RE2 정규식엔 없는 문법인데
  304. HRE를 RE2로 "^gli{@}"HRE /((?:^|\\s+|{}))()gli(a|e|i|o|u)()/RE2 RE2가 이해할 수 있는 문법으로 번역해서

    사용하고 있죠.
  305. HRE 룩어라운드 "foo{bar}" "foo{~bar}" 그 중 구현하기 골치 아팠던 룩어라운드에

    대해 얘기해 드리겠습니다.
  306. /^foo/ foo인데 맨 앞 /foo$/ foo인데 맨 뒤 정규식에서 /^/과

    /$/가 무엇을 뜻하는진 다들 잘 아시죠?
  307. /^foo/ foo인데 맨 앞 /foo$/ foo인데 맨 뒤 /^foo/는 "foo"인데

    문자열 맨 앞에 있는 경우에 매치되고
  308. /^foo/ foo인데 맨 앞 /foo$/ foo인데 맨 뒤 /foo$/는 "foo"인데

    문자열 맨 뒤에 있는 경우에 매치됩니다.
  309. "foo{bar}" foo인데 bar 바로 전 "foo{~bar}" foo인데 bar가 아닌 것

    바로 전 "{bar}foo" foo인데 bar 바로 뒤 "{~bar}foo" foo인데 bar가 아닌 것 바로 뒤 룩어라운드도 비슷해요.
  310. "foo{bar}" foo인데 bar 바로 전 "foo{~bar}" foo인데 bar가 아닌 것

    바로 전 "{bar}foo" foo인데 bar 바로 뒤 "{~bar}foo" foo인데 bar가 아닌 것 바로 뒤 "foo{bar}"라고 쓰면 "bar" 바로 앞에 있는 "foo"에만 매치되고
  311. "foo{bar}" foo인데 bar 바로 전 "foo{~bar}" foo인데 bar가 아닌 것

    바로 전 "{bar}foo" foo인데 bar 바로 뒤 "{~bar}foo" foo인데 bar가 아닌 것 바로 뒤 "foo{~bar}"라고 쓰면 "bar"가 아닌 것 바로 앞에 있는 "foo"에만 매치되죠.
  312. "foo{bar}" foo인데 bar 바로 전 "foo{~bar}" foo인데 bar가 아닌 것

    바로 전 "{bar}foo" foo인데 bar 바로 뒤 "{~bar}foo" foo인데 bar가 아닌 것 바로 뒤 "{bar}foo", "{~bar}foo" 처럼 "{}"가 앞에 올 수도 있습니다.
  313. "foo{bar}" 포지티브 룩어헤드 "foo{~bar}" 네거티브 룩어헤드 "{bar}foo" 포지티브 룩비하인드 "{~bar}foo"

    네거티브 룩비하인드 각각의 문법을 "포지티브 룩어헤드", "네거티브 룩어헤드",
  314. "foo{bar}" 포지티브 룩어헤드 "foo{~bar}" 네거티브 룩어헤드 "{bar}foo" 포지티브 룩비하인드 "{~bar}foo"

    네거티브 룩비하인드 "포지티브 룩비하인드", "네거티브 룩비하인드"라고 부릅니다.
  315. ".^" foo foobar foobaz foobarr foofoobar

  316. ".^" foo foobar foobaz foobarr foofoobar 룩어라운드가 실제로 매치되는 모습을

    한 번 보겠습니다.
  317. ".^" foo foobar foobaz foobarr foofoobar 위쪽은 패턴이고 아래쪽은 대상

    문자열이에요.
  318. ".^" foo foobar foobaz foobarr foofoobar ".^"은 아무 데도 매치되지

    않는 모순 패턴이죠.
  319. "foo" foo foobar foobaz foobarr foofoobar 패턴 "foo"를 찾으면 모든

    "foo"에 불이 들어옵니다.
  320. "foo(bar)" foo foobar foobaz foobarr foofoobar "foo" 뒤에 "(bar)"를 붙이면

    "foobar"에 불이 들어오고
  321. "foo{bar}" foo foobar foobaz foobarr foofoobar "(bar)" 대신 "{bar}"를 써서

    포지티브 룩어헤드로 만들면
  322. "foo{bar}" foo foobar foobaz foobarr foofoobar "foobar" 중 "foo"에만 불이

    들어옵니다.
  323. "foo{bar}" foo foobar foobaz foobarr foofoobar 이런 식으로 "{}" 속

    단어는 매치에 참고는 되지만 실제 결과에 포함되진 않아요.
  324. "foo{~bar}" foo foobar foobaz foobarr foofoobar "{bar}"에 물결을 붙여 "{~bar}"로

    바꿔서 네거티브 룩어헤드로 만들면
  325. "foo{~bar}" foo foobar foobaz foobarr foofoobar "foobar"가 아닌 곳의 "foo"에만

    불이 들어옵니다.
  326. •어두의 n은 초성 ㄴ으로 치환 •모음 뒤 n은 종성 ㅇ으로

    치환 •m 뒤 n은 묵음이니 삭제 룩어라운드 쓰임새 한글라이즈에선 이런 룩어라운드가 꽤 자주 쓰이는데
  327. •어두의 n은 초성 ㄴ으로 치환 •모음 뒤 n은 종성 ㅇ으로

    치환 •m 뒤 n은 묵음이니 삭제 룩어라운드 쓰임새 특히 같은 글자라고 해도 앞뒤 글자에 따라 다르게 바꿔야 하는 경우에 주로 쓰고 있습니다.
  328. /foo(?=bar)/ 포지티브 룩어헤드 /foo(?!bar)/ 네거티브 룩어헤드 /(?<=bar)foo/ 포지티브 룩비하인드 /(?<!bar)foo/

    네거티브 룩비하인드 PCRE 룩어라운드 사실 룩어라운드는 HRE에만 있는 게 아니고 PCRE에서 따온 것입니다.
  329. /foo(?=bar)/ 포지티브 룩어헤드 /foo(?!bar)/ 네거티브 룩어헤드 /(?<=bar)foo/ 포지티브 룩비하인드 /(?<!bar)foo/

    네거티브 룩비하인드 PCRE 룩어라운드 여기선 룩어라운드를 /(?=)/, /(?<!)/ 등으로 표현하는데
  330. /foo(?=bar)/ 포지티브 룩어헤드 /foo(?!bar)/ 네거티브 룩어헤드 /(?<=bar)foo/ 포지티브 룩비하인드 /(?<!bar)foo/

    네거티브 룩비하인드 PCRE 룩어라운드 문법이 다소 어려워서 "{}"와 "~"로 좀 더 쉽게 표현했던 거죠.
  331. /foo(?=bar)/ 포지티브 룩어헤드 /foo(?!bar)/ 네거티브 룩어헤드 /(?<=bar)foo/ 포지티브 룩비하인드 /(?<!bar)foo/

    네거티브 룩비하인드 PCRE 룩어라운드 파이썬 re는 PCRE 룩어라운드를 지원해서 옛 한글라이즈에선 "{}" 문법을 단순히
  332. /foo(?=bar)/ 포지티브 룩어헤드 /foo(?!bar)/ 네거티브 룩어헤드 /(?<=bar)foo/ 포지티브 룩비하인드 /(?<!bar)foo/

    네거티브 룩비하인드 PCRE 룩어라운드 PCRE 문법으로 번역하는 것만으로 쉽게 룩어라운드를 구현할 수 있었습니다.
  333. •안전한 정규표현식 구현 •문자열 길이에 대해 선형 시간 •고의적으로 일부

    기능 미지원 RE2 by Google 그런데 아까 RE2가 고의적으로 일부 기능을 지원하지 않는다고 말씀 드렸잖아요?
  334. 룩어라운드 사용 불가 (?=bar) 룩어라운드도 그 중 하나였습니다.

  335. 룩어라운드 사용 불가 (?=bar) 옛 한글라이즈에서처럼 단순한 번역으로는 구현할 수

    없었죠.
  336. import "github.com/glenn-brown/.../pcre" import "github.com/dlclark/regexp2" 찾아보니 서드파티 라이브러리 중에

  337. import "github.com/glenn-brown/.../pcre" import "github.com/dlclark/regexp2" PCRE나 .NET을 기반으로 한 정규식 엔진도

    발견할 수 있었는데
  338. import "github.com/glenn-brown/.../pcre" import "github.com/dlclark/regexp2" 이런 라이브러리를 쓰면 정규식의 모든 기능을

    쓸 수 있기야 하지만
  339. 이왕이면 표준 이왕이면 표준 정규식 라이브러리를 쓰고 싶어서

  340. 이왕이면 표준 RE2만으로 룩어라운드를 구현할 대안을 고민해보게 됐습니다.

  341. "foo{bar}" "{bar}foo" 포지티브 룩어라운드 대안 /()(foo)(bar)/ /(bar)(foo)()/

  342. "foo{bar}" "{bar}foo" 포지티브 룩어라운드 대안 /()(foo)(bar)/ /(bar)(foo)()/ 포지티프 룩어라운드의 대안

    구현은 간단했어요.
  343. "foo{bar}" "{bar}foo" 포지티브 룩어라운드 대안 /()(foo)(bar)/ /(bar)(foo)()/ 그냥 룩어라운드 부분까지

    포함시켜서 매치하고
  344. $2만 취하기 "foo{bar}" "{bar}foo" 포지티브 룩어라운드 대안 /()(foo)(bar)/ /(bar)(foo)()/ $2

    그룹만 취해서 앞뒤를 잘라내는 것으로 충분했습니다.
  345. "foo{~bar}" "{~bar}foo" 네거티브 룩어라운드 대안 /()(foo)()/ /()(foo)()/ 반면 네거티브 룩어라운드는

    살짝 복잡했어요.
  346. "foo{~bar}" "{~bar}foo" 네거티브 룩어라운드 대안 /()(foo)()/ /()(foo)()/ 일단 RE2 용으로

    번역한 정규식에는 "{~}" 부분을 아예 넣지 않았습니다.
  347. "foo{~bar}" "{~bar}foo" 네거티브 룩어라운드 대안 /()(foo)()/ /()(foo)()/ 이 경우 모든

    "foo"에 대해서 매치되겠죠.
  348. "foo{~bar}" "{~bar}foo" 네거티브 룩어라운드 대안 /()(foo)()/ /()(foo)()/ 뒤쪽이 /^bar/이면 건너뛰기

    앞쪽이 /bar$/이면 건너뛰기 배제 패턴 그러고 나서 "{~}" 속에 있는 배제 패턴을 매치됐던 곳 앞뒤에 한 번 더 매치시켜 봅니다.
  349. "foo{~bar}" "{~bar}foo" 네거티브 룩어라운드 대안 /()(foo)()/ /()(foo)()/ 뒤쪽이 /^bar/이면 건너뛰기

    앞쪽이 /bar$/이면 건너뛰기 배제 패턴 만약 앞뒤가 이 패턴에 매치되면 최종 결과에선 빠지는 거죠.
  350. "foo{~bar}" "{~bar}foo" 네거티브 룩어라운드 대안 /()(foo)()/ /()(foo)()/ 뒤 3글자가 /^bar/이면

    건너뛰기 앞 3글자가 /bar$/이면 건너뛰기 bar에 맞춰서 길이 제한 이때 살펴보는 앞뒤의 길이를
  351. "foo{~bar}" "{~bar}foo" 네거티브 룩어라운드 대안 /()(foo)()/ /()(foo)()/ 뒤 3글자가 /^bar/이면

    건너뛰기 앞 3글자가 /bar$/이면 건너뛰기 bar에 맞춰서 길이 제한 배제 패턴이 매치될 수 있을 만한 최대 길이로 제한하고 있어요.
  352. "foo{~bar}" "{~bar}foo" 네거티브 룩어라운드 대안 /()(foo)()/ /()(foo)()/ 뒤 3글자가 /^bar/이면

    건너뛰기 앞 3글자가 /bar$/이면 건너뛰기 bar에 맞춰서 길이 제한 예시로 든 배제 패턴은 /^bar/, /bar$/인데 앞뒤가 "bar"인지 아닌지는
  353. "foo{~bar}" "{~bar}foo" 네거티브 룩어라운드 대안 /()(foo)()/ /()(foo)()/ 뒤 3글자가 /^bar/이면

    건너뛰기 앞 3글자가 /bar$/이면 건너뛰기 bar에 맞춰서 길이 제한 3글자만 보면 되니까 딱 그만큼만 보는 거죠.
  354. "foo{~bar}" "{~bar}foo" 네거티브 룩어라운드 대안 /()(foo)()/ /()(foo)()/ 뒤 3글자가 /^bar/이면

    건너뛰기 앞 3글자가 /bar$/이면 건너뛰기 bar에 맞춰서 길이 제한 이 길이 제한이 있어야만 대상 문자열이 아무리 길어져도
  355. "foo{~bar}" "{~bar}foo" 네거티브 룩어라운드 대안 /()(foo)()/ /()(foo)()/ 뒤 3글자가 /^bar/이면

    건너뛰기 앞 3글자가 /bar$/이면 건너뛰기 bar에 맞춰서 길이 제한 네거티브 룩어라운드를 빠르게 끝낼 수 있습니다.
  356. syntax.Parse("a?|b", syntax.Perl) import "regexp/syntax" Alternate '|' Literal 'b' Quest '?'

    Literal 'a' 패턴이 매치될 수 있는 문자열의 최대 길이를 구하는 건 간단했습니다.
  357. syntax.Parse("a?|b", syntax.Perl) import "regexp/syntax" Alternate '|' Literal 'b' Quest '?'

    Literal 'a' regexp/syntax 표준 라이브러를 쓰면 정규식을 해석한 추상구문트리를 얻을 수 있는데
  358. Alternate '|' Literal 'b' Quest '?' Literal 'a' 트리 살펴보면

    최대 길이 계산 가능 트리 노드를 적당히 살펴보는 것만으로 최대 길이를 쉽게 계산할 수 있었죠.
  359. "{~a}foo" O(n) "{~a+}foo" O(n²) 네거티브 룩어라운드 성능 물론 배제 패턴의

    매치 최대 길이가 무한대인 경우엔
  360. "{~a}foo" O(n) "{~a+}foo" O(n²) 네거티브 룩어라운드 성능 앞뒤를 딱 필요한

    만큼 자를 수 없어서 최악의 경우 제곱 시간이 걸릴 수도 있어요.
  361. "{~a+}foo" O(n²) 이런 패턴은 금지 하지만 다행히 이런 패턴은 그동안

    한 번도 쓰였던 적이 없었고
  362. "{~a+}foo" O(n²) 이런 패턴은 금지 간단히 금지하기로 결정할 수 있었습니다.

  363. /()(foo)()/ foo foobar foobaz foobarr foofoobar 뒤 3글자가 /^bar/이면 건너뛰기

  364. /()(foo)()/ foo foobar foobaz foobarr foofoobar 뒤 3글자가 /^bar/이면 건너뛰기

    이렇게 만들어진 한글라이즈의 네거티브 룩어라운드가
  365. /()(foo)()/ foo foobar foobaz foobarr foofoobar 뒤 3글자가 /^bar/이면 건너뛰기

    실제로 어떻게 돌아가는지 한 번 살펴보겠습니다.
  366. /()(foo)()/ foo foobar foobaz foobarr foofoobar 뒤 3글자가 /^bar/이면 건너뛰기

    우선 번역한 정규식으로 매치를 돌려봅니다. 모든 "foo"가 매치되겠죠.
  367. /()(foo)()/ foo foobar foobaz foobarr foofoobar 뒤 3글자가 /^bar/이면 건너뛰기

    그 다음엔 매치된 "foo" 뒤 3글자씩을 살펴봅니다.
  368. /()(foo)()/ foo foobar foobaz foobarr foofoobar 뒤 3글자가 /^bar/이면 건너뛰기

    그 중에 /^bar/에 매치되는 게 있으면
  369. /()(foo)()/ foo foobar foobaz foobarr foofoobar 뒤 3글자가 /^bar/이면 건너뛰기

    결과에서 지워요.
  370. /()(foo)()/ foo foobar foobaz foobarr foofoobar 뒤 3글자가 /^bar/이면 건너뛰기

    이런 과정을 통하면 최종적으로 "bar"가 아닌 것 앞에 있는 "foo"만 남게 됩니다.
  371. b. 말과 글

  372. b. 말과 글 이번엔 한글라이즈에서 말과 글을 다루는 데 도움됐던

  373. b. 말과 글 세 가지 Go 라이브러리를 소개해 드리겠습니다.

  374. •한글 모아쓰기 •중국어 병음 사전 •일본어 형태소 분석 필요한 기능

    필요한 기능은 한글 모아쓰기, 중국어 병음 사전, 일본어 형태소 분석이었습니다.
  375. 한글 모아쓰기 ㅐㅍ-ㄹ 받침 표시 한글 모아쓰기는 이런 걸 얘기해요.

  376. 한글 모아쓰기 ㅐㅍ-ㄹ 받침 표시 "ㅐㅍ-ㄹ"과 같이 자모를 나열했을 때

  377. 한글 모아쓰기 애 ㅐㅍ-ㄹ 받침 표시 불완전한 모음인 "ㅐ" 앞엔

    "ㅇ"을 채워주고
  378. 한글 모아쓰기 애플 ㅐㅍ-ㄹ 받침 표시 불완전한 자음인 "ㅍ" 뒤엔

    "ㅡ"를 채워서
  379. 한글 모아쓰기 애플 ㅐㅍ-ㄹ 받침 표시 온전한 한글 글자로 합쳐주는

    기능입니다.
  380. Διόνυσος 디오니소스 διονισοσ Transcribe ㄷㅣㅗㄴㅣㅅㅗㅅ Rewrite P 한글라이즈 파이프라인의 Transcribe

    단계에서 나온 자모의 나열을 최종결과로 만드는 데 쓰이죠.
  381. suapapa/go_hangul 여기엔 이호민 님이 만드신 "go_hangul"을 사용했습니다.

  382. • 한글 판별 •자모 분리 및 조립 • 한글 획

    세기 import "github.com/suapapa/go_hangul" hangul.Join('ㅎ', 'ㅏ', 'ㄴ') == '한' hangul.Split('한') == 'ㅎ', 'ㅏ', 'ㄴ' hangul.IsJaeum('ㅋ') == true hangul.IsMoeum('ㅋ') == false 호민 님 감사합니다. 이 라이브러리엔 한글을 다루기 위한 유용한 기능이 많이 있어요.
  383. • 한글 판별 •자모 분리 및 조립 • 한글 획

    세기 import "github.com/suapapa/go_hangul" hangul.Join('ㅎ', 'ㅏ', 'ㄴ') == '한' hangul.Split('한') == 'ㅎ', 'ㅏ', 'ㄴ' hangul.IsJaeum('ㅋ') == true hangul.IsMoeum('ㅋ') == false 호민 님 감사합니다. 주어진 문자가 한글인지, 자음인지, 모음인지 판별해주는 함수와
  384. • 한글 판별 •자모 분리 및 조립 • 한글 획

    세기 import "github.com/suapapa/go_hangul" hangul.Join('ㅎ', 'ㅏ', 'ㄴ') == '한' hangul.Split('한') == 'ㅎ', 'ㅏ', 'ㄴ' hangul.IsJaeum('ㅋ') == true hangul.IsMoeum('ㅋ') == false 호민 님 감사합니다. 한글을 자모 단위로 쪼개거나 합치는 기능을 포함해 여러가지를 제공합니다.
  385. • 한글 판별 •자모 분리 및 조립 • 한글 획

    세기 import "github.com/suapapa/go_hangul" hangul.Join('ㅎ', 'ㅏ', 'ㄴ') == '한' hangul.Split('한') == 'ㅎ', 'ㅏ', 'ㄴ' hangul.IsJaeum('ㅋ') == true hangul.IsMoeum('ㅋ') == false 호민 님 감사합니다. 특이하게 한글의 획을 세는 기능까지도 있더라고요.
  386. Rewrite Transcribe Phonemize 四川 쓰촨 si chuan si, chwan, ㅆㅡㅊㅘ-ㄴ

  387. Rewrite Transcribe Phonemize 四川 쓰촨 si chuan si, chwan, ㅆㅡㅊㅘ-ㄴ

    나머지 두 라이브러리는 Phonemize 단계에서 소리글자를 만드는 데에 쓰고 있습니다.
  388. 중국어 병음 Qīng dǎo 青岛 칭 다오 그 중 첫

    번째는 중국어 병음입니다.
  389. 중국어 병음 Qīng dǎo 青岛 칭 다오 중국어 한자엔 대응되는

    로마자 표기가 있는데 이를 "병음"이라고 불러요.
  390. 중국어 병음 Qīng dǎo 青岛 칭 다오 예를 들어 "青岛"의

    병음은 "Qīng dǎo"로 표현되죠.
  391. •중국 한자를 병음으로 변환 •단순 사전이라 빠르다. •의외로 용량도 안

    크다. (800KB) import "github.com/mozillazg/go-pinyin" args := pinyin.NewArgs() pinyin.SinglePinyin('青', args) == "Qīng" "go-pinyin"이라는 라이브러리를 쓰면 손쉽게 병음을 구할 수 있습니다.
  392. •중국 한자를 병음으로 변환 •단순 사전이라 빠르다. •의외로 용량도 안

    크다. (800KB) import "github.com/mozillazg/go-pinyin" args := pinyin.NewArgs() pinyin.SinglePinyin('青', args) == "Qīng" 이 라이브러리는 단순한 사전이어서 속도도 빠르고
  393. •중국 한자를 병음으로 변환 •단순 사전이라 빠르다. •의외로 용량도 안

    크다. (800KB) import "github.com/mozillazg/go-pinyin" args := pinyin.NewArgs() pinyin.SinglePinyin('青', args) == "Qīng" 의외로 데이터도 별로 크지 않아서 800KB 밖에 되지 않습니다.
  394. Hangulize("chi", "北京") Hangulize("chi", "章子怡") == "베이징" == "장쯔이" 이 라이브러리

    덕분에 한글라이즈에 중국어를 새로 추가할 수 있게 됐어요.
  395. Hangulize("chi", "李興燮") == "리싱셰" 이 흥 섭 중국어가 되니까 자기

    한자 이름 넣어보는 재미도 나름 쏠쏠하더라고요.
  396. 陸カ審月名検ナレチカ可栄1経断ヒヲカツ宅意へざン丁可ホ 載省末応をじいく行昧7年てぼかせ答予シ告断ハヌカサ武元 争ちぞけみ。26丈なづ広靱増理ソヒタセ士定ワオトヘ整判 べっ集遺ワサヤイ聞出じどれな勝際ずンれ線芸せ年今ロエヒ 常政よのトッ上日やみぎね告創ゅど。作ケヨワ逮氏ッじルへ 地能どぜ祉83派あ織口ケ更探づを車下べひく毎旅まひづゅ意 幕づ務鋼けぶ要情ハリス択著乞伍トごへれ。 일본어 형태소 분석

    일본어 로렘 입숨
  397. 陸カ審月名検ナレチカ可栄1経断ヒヲカツ宅意へざン丁可ホ 載省末応をじいく行昧7年てぼかせ答予シ告断ハヌカサ武元 争ちぞけみ。26丈なづ広靱増理ソヒタセ士定ワオトヘ整判 べっ集遺ワサヤイ聞出じどれな勝際ずンれ線芸せ年今ロエヒ 常政よのトッ上日やみぎね告創ゅど。作ケヨワ逮氏ッじルへ 地能どぜ祉83派あ織口ケ更探づを車下べひく毎旅まひづゅ意 幕づ務鋼けぶ要情ハリス択著乞伍トごへれ。 일본어 형태소 분석

    일본어 로렘 입숨 다음은 일본어인데
  398. 陸カ審月名検ナレチカ可栄1経断ヒヲカツ宅意へざン丁可ホ 載省末応をじいく行昧7年てぼかせ答予シ告断ハヌカサ武元 争ちぞけみ。26丈なづ広靱増理ソヒタセ士定ワオトヘ整判 べっ集遺ワサヤイ聞出じどれな勝際ずンれ線芸せ年今ロエヒ 常政よのトッ上日やみぎね告創ゅど。作ケヨワ逮氏ッじルへ 地能どぜ祉83派あ織口ケ更探づを車下べひく毎旅まひづゅ意 幕づ務鋼けぶ要情ハリス択著乞伍トごへれ。 일본어 형태소 분석

    일본어 로렘 입숨 일본어는 특히 처리하기 까다로웠던 것 같아요.
  399. 陸カ審月名検ナレチカ可栄1経断ヒヲカツ宅意へざン丁可ホ 載省末応をじいく行昧7年てぼかせ答予シ告断ハヌカサ武元 争ちぞけみ。26丈なづ広靱増理ソヒタセ士定ワオトヘ整判 べっ集遺ワサヤイ聞出じどれな勝際ずンれ線芸せ年今ロエヒ 常政よのトッ上日やみぎね告創ゅど。作ケヨワ逮氏ッじルへ 地能どぜ祉83派あ織口ケ更探づを車下べひく毎旅まひづゅ意 幕づ務鋼けぶ要情ハリス択著乞伍トごへれ。 일본어 형태소 분석

    일본어 로렘 입숨 일본어에선 뜻글자인 한자와 소리글자인 가나를 섞어 씁니다.
  400. 陸カ審月名検ナレチカ可栄1経断ヒヲカツ宅意へざン丁可ホ 載省末応をじいく行昧7年てぼかせ答予シ告断ハヌカサ武元 争ちぞけみ。26丈なづ広靱増理ソヒタセ士定ワオトヘ整判 べっ集遺ワサヤイ聞出じどれな勝際ずンれ線芸せ年今ロエヒ 常政よのトッ上日やみぎね告創ゅど。作ケヨワ逮氏ッじルへ 地能どぜ祉83派あ織口ケ更探づを車下べひく毎旅まひづゅ意 幕づ務鋼けぶ要情ハリス択著乞伍トごへれ。 일본어 형태소 분석

    일본어 로렘 입숨 그리고 띄어쓰기를 사용하지 않아요.
  401. 陸カ審月名検ナレチカ可栄1経断ヒヲカツ宅意へざン丁可ホ 載省末応をじいく行昧7年てぼかせ答予シ告断ハヌカサ武元 争ちぞけみ。26丈なづ広靱増理ソヒタセ士定ワオトヘ整判 べっ集遺ワサヤイ聞出じどれな勝際ずンれ線芸せ年今ロエヒ 常政よのトッ上日やみぎね告創ゅど。作ケヨワ逮氏ッじルへ 地能どぜ祉83派あ織口ケ更探づを車下べひく毎旅まひづゅ意 幕づ務鋼けぶ要情ハリス択著乞伍トごへれ。 일본어 형태소 분석

    일본어 로렘 입숨 여기 붙여 둔 문단은 일본어 로렘 입숨인데 보시다시피 띄어쓰기가 없습니다.
  402. 陸カ審月名検ナレチカ可栄1経断ヒヲカツ宅意へざン丁可ホ 載省末応をじいく行昧7年てぼかせ答予シ告断ハヌカサ武元 争ちぞけみ。26丈なづ広靱増理ソヒタセ士定ワオトヘ整判 べっ集遺ワサヤイ聞出じどれな勝際ずンれ線芸せ年今ロエヒ 常政よのトッ上日やみぎね告創ゅど。作ケヨワ逮氏ッじルへ 地能どぜ祉83派あ織口ケ更探づを車下べひく毎旅まひづゅ意 幕づ務鋼けぶ要情ハリス択著乞伍トごへれ。 일본어 형태소 분석

    일본어 로렘 입숨 전각문자 마침표와 띄어쓰기처럼 보이는 이것도 사실은 전각문자 1개죠.
  403. 일본어 형태소 분석: 띄어쓰기 宮本茂일본어 ▶ 미야모토 시게루 일본인 이름은

    이렇게 한자 서너자로 이뤄진 경우가 많은데
  404. 일본어 형태소 분석: 띄어쓰기 宮本茂일본어 ▶ 미야모토 시게루 어디까지가 성이고

    어디까지가 이름인지 구별해서
  405. 일본어 형태소 분석: 띄어쓰기 宮本茂일본어 ▶ 미야모토 시게루 전사 결과엔

    띄어쓰기를 넣어줘야 합니다.
  406. 일본어 형태소 분석: 띄어쓰기 宮本茂일본어 ▶ 미야모토 시게루 형태소를 분석해보면

    이름 경계를 알 수 있는데
  407. 일본어 형태소 분석: 띄어쓰기 宮本茂일본어 ▶ 미야모토 시게루 이게 일본어에

    형태소 분석이 필요했던 첫 번째 이유죠.
  408. 일본어 형태소 분석: 후리가나 ニッポンダ ニホンゴ 日本だ 日本語 니 고

    혼 닛 폰 다 또 다른 이유는 후리가나입니다.
  409. 일본어 형태소 분석: 후리가나 ニッポンダ ニホンゴ 日本だ 日本語 니 고

    혼 닛 폰 다 일본어에서 한자의 읽는 법을 소리글자로 쓰는 걸 "후리가나"라고 하는데
  410. 일본어 형태소 분석: 후리가나 ニッポンダ ニホンゴ 日本だ 日本語 니 고

    혼 닛 폰 다 같은 한자라도 문맥에 따라서 후리가나가 달라질 수 있습니다.
  411. 일본어 형태소 분석: 후리가나 ニッポンダ ニホンゴ 日本だ 日本語 니 고

    혼 닛 폰 다 한자어인 "日本"의 경우 독립적으로 쓰일 땐 "닛폰"으로 읽히지만
  412. 일본어 형태소 분석: 후리가나 ニッポンダ ニホンゴ 日本だ 日本語 니 고

    혼 닛 폰 다 다른 한자어의 일부로 쓰일 땐 "니혼"으로 읽히더라고요.
  413. 일본어 형태소 분석: 후리가나 ニッポンダ ニホンゴ 日本だ 日本語 니 고

    혼 닛 폰 다 이런 걸 판단해서 한자로부터 후리가나를 추출해줄 도구가 필요했죠.
  414. 일본어 형태소 분석: 장모음 イーエ カワイ·イ いいえ かわいい 가 이

    i i e i i wa ka 에 와 이 이 마지막으로 장모음 문제도 있었습니다.
  415. 일본어 형태소 분석: 장모음 イーエ カワイ·イ いいえ かわいい 가 이

    i i e i i wa ka 에 와 이 이 우리말에선 사라진 장모음/단모음 구별이 일본어엔 있는데
  416. 일본어 형태소 분석: 장모음 イーエ カワイ·イ いいえ かわいい 가 이

    i i e i i wa ka 에 와 이 이 외래어 표기법에선 장모음을 단모음처럼 취급하거든요.
  417. 일본어 형태소 분석: 장모음 イーエ カワイ·イ いいえ かわいい 가 이

    i i e i i wa ka 에 와 이 이 그래서 첫 번째 예시인 "いいえ"(iie)의 경우 "いい"(ii)가 장음이라서
  418. 일본어 형태소 분석: 장모음 イーエ カワイ·イ いいえ かわいい 가 이

    i i e i i wa ka 에 와 이 이 "이이에"가 아닌 "이에"로 옮깁니다.
  419. 일본어 형태소 분석: 장모음 イーエ カワイ·イ いいえ かわいい 가 이

    i i e i i wa ka 에 와 이 이 하지만 똑같이 "いい"(ii)라고 써있더라도 장모음이 아니라
  420. 일본어 형태소 분석: 장모음 イーエ カワイ·イ いいえ かわいい 가 이

    i i e i i wa ka 에 와 이 이 형태소의 경계로 나뉘어 있는 경우도 있어요.
  421. 일본어 형태소 분석: 장모음 イーエ カワイ·イ いいえ かわいい 가 이

    i i e i i wa ka 에 와 이 이 두 번째 예시인 "かわいい"(kawaii)가 그런 경우인데
  422. 일본어 형태소 분석: 장모음 イーエ カワイ·イ いいえ かわいい 가 이

    i i e i i wa ka 에 와 이 이 여기의 "いい"(ii)는 장모음이 아니고 각각 다른 형태소에 속한 두 개의 "い"(i)죠.
  423. 일본어 형태소 분석: 장모음 イーエ カワイ·イ いいえ かわいい 가 이

    i i e i i wa ka 에 와 이 이 이때는 단모음처럼 합치면 안 되고 "이이"라고 구별해서 적어줘야 됩니다.
  424. 일본어 형태소 분석: 장모음 イーエ カワイ·イ いいえ かわいい 가 이

    i i e i i wa ka 에 와 이 이 이처럼 일본어는 제대로 한글로 옮기려면 꽤 정밀한 형태소 분석을 필요로 하는데
  425. •순수 Go •일본어 형태소 분석기 import "github.com/ikawaha/kagome.ipadic" 다행히 "kagome"라는 적합한

    라이브러리가 있었습니다.
  426. •순수 Go •일본어 형태소 분석기 import "github.com/ikawaha/kagome.ipadic" kagome는 Go만으로 구현된

    일본어 형태소 분석기인데
  427. •순수 Go •일본어 형태소 분석기 import "github.com/ikawaha/kagome.ipadic" 여기에 일본어 문장을

    입력하면
  428. •순수 Go •일본어 형태소 분석기 import "github.com/ikawaha/kagome.ipadic" 형태소의 품사, 원형,

    읽는 법까지 꽤 자세한 정보를 구할 수 있어요.
  429. •순수 Go •일본어 형태소 분석기 •무거워요 import "github.com/ikawaha/kagome.ipadic" 단, 다소

    무거운 편입니다.
  430. •순수 Go •일본어 형태소 분석기 •무거워요 import "github.com/ikawaha/kagome.ipadic" 초기화 속도도

    느리고 분석 속도도 그리 빠르진 않더라고요.
  431. Hangulize("jpn", "任天堂") Hangulize("jpn", "新海誠") Hangulize("jpn", "天空の城ラピュタ") == "닌텐도" == "신카이

    마코토" == "덴쿠노시로라퓨타" 어쨌든 옛 한글라이즈의 일본어 규칙에선
  432. Hangulize("jpn", "任天堂") Hangulize("jpn", "新海誠") Hangulize("jpn", "天空の城ラピュタ") == "닌텐도" == "신카이

    마코토" == "덴쿠노시로라퓨타" 소리글자인 가나만 지원했고 장모음 구별도 못 했는데
  433. Hangulize("jpn", "任天堂") Hangulize("jpn", "新海誠") Hangulize("jpn", "天空の城ラピュタ") == "닌텐도" == "신카이

    마코토" == "덴쿠노시로라퓨타" kagome 덕분에 새 한글라이즈는 웬만한 일본어 문장까지도 지원할 수 있게 됐습니다.
  434. •hangulize 8.1MB •go-pinyin 3.3MB •kagome.ipadic 13MB •합치면 20MB 빌드 용량

  435. •hangulize 8.1MB •go-pinyin 3.3MB •kagome.ipadic 13MB •합치면 20MB 빌드 용량

    그런데 go-pinyin이랑 kagome까지 한글라이즈 패키지에 포함하려고 보니
  436. •hangulize 8.1MB •go-pinyin 3.3MB •kagome.ipadic 13MB •합치면 20MB 빌드 용량

    빌드 용량이 너무 커지더라고요.
  437. •hangulize 8.1MB •go-pinyin 3.3MB •kagome.ipadic 13MB •합치면 20MB 빌드 용량

    특히 JavaScript 빌드도 같이 뽑고 있었는데 20MB면 JS 파일 하나치곤 너무 커서
  438. import "github.com/hangulize/hangulize" import "github.com/hangulize/hangulize/phonemize/furigana" func main() { hangulize.UsePhonemizer(&furigana.P) fmt.Println(hangulize.Hangulize("jpn", "秋葉原"))

    // Output: 아키하바라 } 플러그인 이렇게 플러그인처럼 쓸 수 있도록 분리했어요.
  439. import "github.com/hangulize/hangulize" import "github.com/hangulize/hangulize/phonemize/furigana" func main() { hangulize.UsePhonemizer(&furigana.P) fmt.Println(hangulize.Hangulize("jpn", "秋葉原"))

    // Output: 아키하바라 } 플러그인 여러분이 Go 코드에서 한글라이즈 라이브러리를 쓰실 경우엔
  440. import "github.com/hangulize/hangulize" import "github.com/hangulize/hangulize/phonemize/furigana" func main() { hangulize.UsePhonemizer(&furigana.P) fmt.Println(hangulize.Hangulize("jpn", "秋葉原"))

    // Output: 아키하바라 } 플러그인 직접 UsePhonemizer를 호출해야만 중국어와 일본어도 제대로 다룰 수 있습니다.
  441. c. 테스트와 문서화

  442. c. 테스트와 문서화 다음으로 Go의 테스트 및 문서화 도구에서 인상적이었던

    부분을 말씀드릴게요.
  443. assert Portuguese({ 'bossa nova': '보사 노바', 'moor': '모르', 'maracas': '마라카스',

    }) 용례집 유닛테스트 국립국어원 사이트에선 이렇게 외래어 표기법 용례집을 구할 수 있는데
  444. assert Portuguese({ 'bossa nova': '보사 노바', 'moor': '모르', 'maracas': '마라카스',

    }) 용례집 유닛테스트 옛 한글라이즈 때부터 이 용례집을 유닛테스트로 만들어 써왔어요.
  445. assert Portuguese({ 'bossa nova': '보사 노바', 'moor': '모르', 'maracas': '마라카스',

    }) 용례집 유닛테스트 이것으로 전사 규칙을 제대로 만들었는지 쉽게 검증할 수 있었죠.
  446. 기존 용례집 테스트 승계 5. 20. 재제작 시작 5. 27.

    첫 릴리즈 5. 28. 기존 언어 모두 지원 8. 27. 발표 새 한글라이즈를 만들면서도 기존 용례집 테스트는 그대로 승계했습니다.
  447. 기존 용례집 테스트 승계 5. 20. 재제작 시작 5. 27.

    첫 릴리즈 5. 28. 기존 언어 모두 지원 8. 27. 발표 처음부터 테스트가 충분했던 덕분에
  448. 기존 용례집 테스트 승계 5. 20. 재제작 시작 5. 27.

    첫 릴리즈 5. 28. 기존 언어 모두 지원 8. 27. 발표 8일 첫 8일만에 기존 38가지 언어를 모두 지원하는 것도 가능했죠.
  449. 기존 용례집 테스트 승계 5. 20. 재제작 시작 5. 27.

    첫 릴리즈 5. 28. 기존 언어 모두 지원 8. 27. 발표 8일 그만큼 한글라이즈에서 테스트는 굉장히 중요한 축을 맡고 있습니다.
  450. import "testing"

  451. import "testing" "testing"은 Go의 표준 테스트 도구인데 기본 도구임에도 불구하고

  452. import "testing" 상당히 강력한 테스트 프레임워크를 쓰는 기분이었습니다.

  453. // $ go test -run TestLang/ita func TestLang(t *testing.T) {

    t.Run("jpn", testJpn) t.Run("chi", testChi) t.Run("ita", testIta) } 서브테스트 testing에서 인상적인 점 중 하나는 바로 서브테스트 기능이었어요.
  454. // $ go test -run TestLang/ita func TestLang(t *testing.T) {

    t.Run("jpn", testJpn) t.Run("chi", testChi) t.Run("ita", testIta) } 서브테스트 서브테스트는 한 테스트케이스에서 여러 테스트케이스를 파생시키는 기능인데
  455. // $ go test -run TestLang/ita func TestLang(t *testing.T) {

    t.Run("jpn", testJpn) t.Run("chi", testChi) t.Run("ita", testIta) } 서브테스트 본격적인 서드파티 테스트 프레임워크에서나 지원할 법한 걸
  456. // $ go test -run TestLang/ita func TestLang(t *testing.T) {

    t.Run("jpn", testJpn) t.Run("chi", testChi) t.Run("ita", testIta) } 서브테스트 기본 도구가 지원한다는 점이 놀라웠죠.
  457. // $ go test -run TestLang/ita func TestLang(t *testing.T) {

    t.Run("jpn", testJpn) t.Run("chi", testChi) t.Run("ita", testIta) } 서브테스트 한글라이즈에선 각 언어 별 용례집을 검증하는 테스트를
  458. // $ go test -run TestLang/ita func TestLang(t *testing.T) {

    t.Run("jpn", testJpn) t.Run("chi", testChi) t.Run("ita", testIta) } 서브테스트 이런 식으로 서브테스트를 이용해 만들었습니다.
  459. // $ go test -bench . // BenchmarkF 100 1078834

    ns/op func BenchmarkF(b *testing.B) { for i := 0; i < b.N; i++ { f() } } 벤치마크
  460. // $ go test -bench . // BenchmarkF 100 1078834

    ns/op func BenchmarkF(b *testing.B) { for i := 0; i < b.N; i++ { f() } } 벤치마크 또 벤치마크 기능까지 제공하는 점도 신선했는데
  461. // $ go test -bench . // BenchmarkF 100 1078834

    ns/op func BenchmarkF(b *testing.B) { for i := 0; i < b.N; i++ { f() } } 벤치마크 "Benchmark"로 시작하는 함수에서 testing.B를 인자로 받아
  462. // $ go test -bench . // BenchmarkF 100 1078834

    ns/op func BenchmarkF(b *testing.B) { for i := 0; i < b.N; i++ { f() } } 벤치마크 b.N바퀴 도는 반복문 코드를 작성하는 식으로 만들더라고요.
  463. // $ go test -bench . // BenchmarkF 100 1078834

    ns/op func BenchmarkF(b *testing.B) { for i := 0; i < b.N; i++ { f() } } 벤치마크 테스트 실행할 때 "-bench" 옵션을 지정하면
  464. // $ go test -bench . // BenchmarkF 100 1078834

    ns/op func BenchmarkF(b *testing.B) { for i := 0; i < b.N; i++ { f() } } 벤치마크 벤치마크를 돌려본 후 루프 한 바퀴에 얼마나 걸렸는지 알려줍니다.
  465. benchmark( setup='prepare()', run='do_it()', ) 흔히 보던 벤치마크 제가 흔히 보던

    벤치마크 도구에선 보통
  466. benchmark( setup='prepare()', run='do_it()', ) 흔히 보던 벤치마크 벤치마크를 준비하는 코드

    "setup"과 벤치마크할 코드 "run"을
  467. benchmark( setup='prepare()', run='do_it()', ) 흔히 보던 벤치마크 따로 입력하는 인터페이스가

    있었는데
  468. func BenchmarkDoIt(b *testing.B) { Prepare() b.ResetTimer() for i := 0;

    i < b.N; i++ { DoIt() } } Go 벤치마크 복잡한 추상화 없이 testing.B 인자만으로
  469. func BenchmarkDoIt(b *testing.B) { Prepare() b.ResetTimer() for i := 0;

    i < b.N; i++ { DoIt() } } Go 벤치마크 훨씬 간결하게 같은 기능을 구현한다는 점에서 상당히 감탄했습니다.
  470. func BenchmarkF(b *testing.B) { b.Run("1", genBenchmarkF(1)) b.Run("10", genBenchmarkF(10)) b.Run("100", genBenchmarkF(100))

    b.Run("1000", genBenchmarkF(1000)) } 서브벤치마크 BenchmarkF/1 BenchmarkF/1000 시간
  471. func BenchmarkF(b *testing.B) { b.Run("1", genBenchmarkF(1)) b.Run("10", genBenchmarkF(10)) b.Run("100", genBenchmarkF(100))

    b.Run("1000", genBenchmarkF(1000)) } 서브벤치마크 BenchmarkF/1 BenchmarkF/1000 시간 벤치마크에서도 서브테스트와 같은 방식으로 서브벤치마크를 만들 수 있는데
  472. func BenchmarkF(b *testing.B) { b.Run("1", genBenchmarkF(1)) b.Run("10", genBenchmarkF(10)) b.Run("100", genBenchmarkF(100))

    b.Run("1000", genBenchmarkF(1000)) } 서브벤치마크 BenchmarkF/1 BenchmarkF/1000 시간 덕분에 알고리즘의 시간복잡도에 더 집중할 수 있었고
  473. func BenchmarkF(b *testing.B) { b.Run("1", genBenchmarkF(1)) b.Run("10", genBenchmarkF(10)) b.Run("100", genBenchmarkF(100))

    b.Run("1000", genBenchmarkF(1000)) } 서브벤치마크 BenchmarkF/1 BenchmarkF/1000 시간 개선하는 과정이 눈에 바로바로 들어와서 성취감도 높았습니다.
  474. func TestF(t *testing.T) { if f() != 42 { t.Fail("f()

    does not return 42") } }
  475. func TestF(t *testing.T) { if f() != 42 { t.Fail("f()

    does not return 42") } } 한 가지 아쉬웠던 건 Go에 assert 문법이 없어서
  476. func TestF(t *testing.T) { if f() != 42 { t.Fail("f()

    does not return 42") } } 테스트케이스를 두꺼운 if문 덩어리로 만들어야 한다는 점이었는데
  477. import "github.com/stretchr/testify/assert" func TestF(t *testing.T) { assert.Equal(t, 42, f()) assert.NotEqual(t,

    21, f()) } 전 테스트케이스를 좀 더 편하게 쓰기 위해 서드파티 라이브러리인 "testify"를 쓰고 있습니다.
  478. import "github.com/stretchr/testify/assert" func TestF(t *testing.T) { assert.Equal(t, 42, f()) assert.NotEqual(t,

    21, f()) } testify의 "assert" 패키지가 제공하는 "Equal", "NotEqual" 같은 메소드를 이용하면
  479. import "github.com/stretchr/testify/assert" func TestF(t *testing.T) { assert.Equal(t, 42, f()) assert.NotEqual(t,

    21, f()) } 테스트 코드 작성도 훨씬 간편하고 테스트 실패 보고서도 읽기 더 좋더라고요.
  480. import "github.com/stretchr/testify/assert" func TestF(t *testing.T) { assert.Equal(t, 42, f()) assert.NotEqual(t,

    21, f()) } 워낙 유용하고 유명해서 아마 여러분 대부분도 이미 쓰고 계실 것 같습니다.
  481. https://godoc.org/github.com/hangulize/hangulize

  482. https://godoc.org/github.com/hangulize/hangulize 한편 패키지를 문서화하면서는 GoDoc의 초간단 문법이 인상적이었어요.

  483. • **이런거**, _이런거_, `이런거` 없음 • 불릿 목록도 없음 •

    그림도 없음 초라한 문법 그동안 마크다운에 익숙해져 있었고 마크다운도 충분히 간단하다고 생각했는데
  484. • **이런거**, _이런거_, `이런거` 없음 • 불릿 목록도 없음 •

    그림도 없음 초라한 문법 GoDoc의 문법은 훨씬 단순하더라고요.
  485. • **이런거**, _이런거_, `이런거` 없음 • 불릿 목록도 없음 •

    그림도 없음 초라한 문법 **볼드**, _이탤릭_, `코드`도 없고 불릿 목록도 없고 그림도 없었습니다.
  486. • URL엔 하이퍼링크 • 들여쓰면 코드블록 • 대문자로 시작하고 마침표가

    없는 줄은 제목 초간단 문법 그렇다고 문서화하는 데에 부족한 건 아니었어요.
  487. • URL엔 하이퍼링크 • 들여쓰면 코드블록 • 대문자로 시작하고 마침표가

    없는 줄은 제목 초간단 문법 URL엔 자동으로 하이퍼링크가 붙고 한 칸이라도 들여쓰기하면 코드블록으로 인식됩니다.
  488. • URL엔 하이퍼링크 • 들여쓰면 코드블록 • 대문자로 시작하고 마침표가

    없는 줄은 제목 초간단 문법 마크다운처럼 "#" 같은 걸 붙이지 않고도
  489. • URL엔 하이퍼링크 • 들여쓰면 코드블록 • 대문자로 시작하고 마침표가

    없는 줄은 제목 초간단 문법 대문자로 시작하고 마침표가 없는 줄은 제목으로 취급되죠.
  490. $ go doc github.com/hangulize/hangulize PACKAGE DOCUMENTATION package hangulize import "github.com/hangulize/hangulize"

    Package hangulize transcribes non-Korean words into Hangul. "Hello!" -> "헬로!" Hangulize was inspired by Brian Jongseong Park (http://iceager.egloos.com/2610028). Based on this idea, the original 커맨드라인 문법이 이렇게 단순한 건 GoDoc이 웹 사이트 뿐만 아니라
  491. $ go doc github.com/hangulize/hangulize PACKAGE DOCUMENTATION package hangulize import "github.com/hangulize/hangulize"

    Package hangulize transcribes non-Korean words into Hangul. "Hello!" -> "헬로!" Hangulize was inspired by Brian Jongseong Park (http://iceager.egloos.com/2610028). Based on this idea, the original 커맨드라인 커맨드라인 출력도 중요하게 다루기 때문이라고 합니다.
  492. •.md, .rst에 비해 초라하지만 •외울 게 적어서 •서식을 헷갈리지 않아요.

    쉬운 문법 아무튼 마크다운이나 reStructuredText에 비하면 상당히 초라한 문법이지만
  493. •.md, .rst에 비해 초라하지만 •외울 게 적어서 •서식을 헷갈리지 않아요.

    쉬운 문법 그만큼 외울 게 적어서 금방 익숙해질 수 있었고
  494. •.md, .rst에 비해 초라하지만 •외울 게 적어서 •서식을 헷갈리지 않아요.

    쉬운 문법 작성하면서 서식을 헷갈리는 일이 거의 없었던 것 같습니다.
  495. $ godoc \ -http=:8080 \ -goroot="$GOPATH" 로컬 godoc localhost:8080/pkg/github.com/hangulize/hangulize 처음

    쓸 때 조금 헷갈리더라도 로컬 GoDoc 서버를 이용해
  496. $ godoc \ -http=:8080 \ -goroot="$GOPATH" 로컬 godoc localhost:8080/pkg/github.com/hangulize/hangulize 실시간으로

    바로바로 확인하면서 쓰다 보니 금새 익힐 수 있었습니다.
  497. func ExampleComposeHangul_perfect() 예제 코드 대상 이름 소제목 예제 코드로 인식되게

  498. func ExampleComposeHangul_perfect() 예제 코드 대상 이름 소제목 예제 코드로 인식되게

    함 또한 문서에 포함되는 예제 코드를 테스트 코드의 일종으로 다루는 것도 인상적이었는데
  499. func ExampleComposeHangul_perfect() 예제 코드 대상 이름 소제목 예제 코드로 인식되게

    함 정해진 형식에 맞춰 테스트 함수의 이름을 지정하면
  500. 문서 상에 딱 그 대상 밑에

  501. 함수 내용이 예제 코드로써 배치되는 방식이었어요.

  502. $ go test --- FAIL: ExampleComposeHangul_perfect (0.00s) got: 하느글라이스 want:

    한글라이즈 FAIL exit status 1 FAIL github.com/hangulize/hangulize 1.949s 예제 코드 방부처리 예제 코드가 테스트 코드의 일종이다 보니 혹시 나중에 그 내용이 틀리게 되더라도
  503. $ go test --- FAIL: ExampleComposeHangul_perfect (0.00s) got: 하느글라이스 want:

    한글라이즈 FAIL exit status 1 FAIL github.com/hangulize/hangulize 1.949s 예제 코드 방부처리 테스트가 바로 실패해서 언제나 올바른 상태를 유지할 수 있도록 도와주더라고요.
  504. 3. 이룬 것

  505. 3. 이룬 것 지금까지 한글라이즈를 Go로 다시 만들면서 인상적이었던 부분을

    소개해드렸는데
  506. 3. 이룬 것 마지막으로 이번 재제작 과정을 통해서 이룬 점들을

    살펴보고 이 발표를 마치겠습니다.
  507. 1. 성능 개선 이룬 것 첫 번째는 성능 개선이에요.

  508. 1. 성능 개선 이룬 것 Go가 파이썬보다 훨씬 빠르기는 하지만

    이번엔 알고리즘에도 꽤 신경쓰면서 작업했거든요.
  509. Cappuccino이탈리아어 ▶ 카푸치노 •옛 한글라이즈 398㎲ •새 한글라이즈 85㎲ "카푸치노"같이

    짧은 단어로 비교해보면 옛 한글라이즈에선 398㎲가 걸렸지만
  510. Cappuccino이탈리아어 ▶ 카푸치노 •옛 한글라이즈 398㎲ •새 한글라이즈 85㎲ 새

    한글라이즈에선 85㎲밖에 걸리지 않았습니다.
  511. Cappuccino이탈리아어 ▶ 카푸치노 •옛 한글라이즈 398㎲ •새 한글라이즈 85㎲ 네다섯

    배는 빨라진 거죠.
  512. 단어 길이 옛 한글라이즈 새 한글라이즈 8만 자 630㎳ 465㎳

    80만 자 10초 5초 800만 자 14분 50초 시간 옛 한글라이즈 새 한글라이즈 단어 길이 단어가 길면 길 수록 새 한글라이즈가 옛 한글라이즈보다 더욱 빠른데
  513. 단어 길이 옛 한글라이즈 새 한글라이즈 8만 자 630㎳ 465㎳

    80만 자 10초 5초 800만 자 14분 50초 시간 옛 한글라이즈 새 한글라이즈 단어 길이 800만 자짜리 단어에서 새 한글라이즈에선 50초 정도밖에 걸리지 않았지만
  514. 단어 길이 옛 한글라이즈 새 한글라이즈 8만 자 630㎳ 465㎳

    80만 자 10초 5초 800만 자 14분 50초 시간 옛 한글라이즈 새 한글라이즈 단어 길이 옛 한글라이즈에선 14분이나 걸리더라고요.
  515. O(n²) O(n) 옛 한글라이즈 새 한글라이즈 옛 한글라이즈엔 일부 비효율적인

    코드가 있어서 시간복잡도가 제곱 시간이었는데
  516. O(n²) O(n) 옛 한글라이즈 새 한글라이즈 그에 반해 새 한글라이즈는

    선형시간을 보장하기 때문입니다.
  517. 2. Phonemize 도입 이룬 것

  518. 2. Phonemize 도입 이룬 것 두 번째는 이번에 새로 추가한

    Phonemize 단계예요.
  519. Rewrite Transcribe Phonemize 재제작 과정에서 새로 도입 四川 쓰촨 si

    chuan si, chwan, ㅆㅡㅊㅘ-ㄴ Phonemize 단계가 옛 한글라이즈엔 없었거든요.
  520. Rewrite Transcribe Phonemize 재제작 과정에서 새로 도입 四川 쓰촨 si

    chuan si, chwan, ㅆㅡㅊㅘ-ㄴ 앞에서 살펴봤듯이 이 단계가 추가된 덕분에
  521. Rewrite Transcribe Phonemize 재제작 과정에서 새로 도입 四川 쓰촨 si

    chuan si, chwan, ㅆㅡㅊㅘ-ㄴ 중국어도 새로 지원할 수 있었고 일본어도 더 제대로 지원할 수 있게 됐죠.
  522. Ghoti fish Phonemize 어쩌면 영어도? 어쩌면 Phonemize 단계를 이용해서 언젠간

    영어도 지원할 수 있지 않을까 싶습니다.
  523. 3. 생산성 개선 이룬 것

  524. 3. 생산성 개선 이룬 것 그리고 전사 규칙 만들 때의

    생산성도 상당히 좋아진 것 같아요.
  525. • 중국어 전혀 모르는데 • 중국어 외래어 표기법 만드는 데

    이틀 전사 규칙 제작 생산성 저는 중국어를 전혀 모르는데 중국어 외래어 표기법 자료만 보고
  526. • 중국어 전혀 모르는데 • 중국어 외래어 표기법 만드는 데

    이틀 전사 규칙 제작 생산성 규칙을 만드는 데엔 이틀 밖에 걸리지 않았습니다.
  527. test: "郭廣昌" -> "궈광창" "劉鶴" -> "류허" "屠呦呦" -> "투유유"

    "許家印" -> "쉬자인" "王健林" -> "왕젠린" "廬志强" -> "루즈창" "馬化騰" -> "마화텅" "努爾白克力" -> "누얼바이커리" "金立群" -> "진리췬" "曹國偉" -> "차오궈웨이" "李河君" -> "리허쥔" "游客" -> "유커" "雷軍" -> "레이쥔" "李保樟" -> "리바오장" "古天樂" -> "구톈러" "鄧森悅" -> "덩썬웨" 이렇게 용례집에서 단어를 긁어서 테스트케이스에 대량으로 집어넣고
  528. rewrite: "v" -> "yu" "{c|ch|r|s|sh|z|zh}i" -> "i," "{<jqx>}ue" -> "yue"

    "{<jqx>}uan" -> "yuan" "{<jqx>}un" -> "yun" transcribe: # 단운 "yu" -> "ㅟ" # 제치류 "{<J>}yang" -> "ㅏ-ㅇ" "{<J>}yan" -> "ㅔ-ㄴ" "{<J>}you" -> "ㅜ" "{<J>}yai" -> "ㅏㅣ" "{<J>}yao" -> "ㅏㅗ" "{<J>}ya" -> "ㅏ" "{<J>}yo" -> "ㅗ" "{<J>}ye" -> "ㅔ" 오른쪽과 같은 외래어 표기법 표를 참고해서
  529. rewrite: "v" -> "yu" "{c|ch|r|s|sh|z|zh}i" -> "i," "{<jqx>}ue" -> "yue"

    "{<jqx>}uan" -> "yuan" "{<jqx>}un" -> "yun" transcribe: # 단운 "yu" -> "ㅟ" # 제치류 "{<J>}yang" -> "ㅏ-ㅇ" "{<J>}yan" -> "ㅔ-ㄴ" "{<J>}you" -> "ㅜ" "{<J>}yai" -> "ㅏㅣ" "{<J>}yao" -> "ㅏㅗ" "{<J>}ya" -> "ㅏ" "{<J>}yo" -> "ㅗ" "{<J>}ye" -> "ㅔ" 패턴 찾아 바꾸기 규칙만 몇 개 짜다 보니 금방 완성할 수 있었죠.
  530. 4. Go 학습 이룬 것

  531. 4. Go 학습 이룬 것 마지막으로 이번 한글라이즈 재제작을 통해서

    이룬 가장 큰 성과는
  532. 4. Go 학습 이룬 것 Go를 많이 익힐 수 있었다는

    것입니다.
  533. •고루틴은 끝내주는데 •그런데 너무 기능이 적다. •고퍼도 귀엽지 않고 Go

    첫인상 Go에 대한 제 첫인상은 이랬죠.
  534. •고루틴은 끝내주는데 •그런데 너무 기능이 적다. •고퍼도 귀엽지 않고 Go

    첫인상 고루틴은 끝내주지만 언어의 기능이 너무 빈약해 보였습니다.
  535. •고루틴은 끝내주는데 •그런데 너무 기능이 적다. •고퍼도 귀엽지 않고 Go

    첫인상 고퍼도 안 귀엽고요.
  536. • 세밀하게 절제된 최소주의 •실수하지 않도록 도와주는 환경 Go 지금

    인상 하지만 지금은, 빈약해 보였던 언어가
  537. • 세밀하게 절제된 최소주의 •실수하지 않도록 도와주는 환경 Go 지금

    인상 사실은 세밀하게 절제된 최소주의를 따르는 것이었고
  538. • 세밀하게 절제된 최소주의 •실수하지 않도록 도와주는 환경 Go 지금

    인상 언어의 복잡성을 극도로 낮춰서
  539. • 세밀하게 절제된 최소주의 •실수하지 않도록 도와주는 환경 Go 지금

    인상 대규모 프로젝트에서도 실수하기 어렵고
  540. • 세밀하게 절제된 최소주의 •실수하지 않도록 도와주는 환경 Go 지금

    인상 조금만 신경 써도 좋은 코드를 작성할 수 있게 해준다는 걸 깨닫게 됐습니다.
  541. • 세밀하게 절제된 최소주의 •실수하지 않도록 도와주는 환경 • 고퍼는

    여전히 귀엽지 않다. Go 지금 인상 그래도 고퍼는 여전히 귀여워 보이지 않지만요.
  542. •패키징 •테스팅 •문서화 •정규표현식 •파서 • 플러그인 • 성능 최적화

    Go에 자신감 이번 경험으로 Go의 여러가지 영역에 입문했고
  543. •패키징 •테스팅 •문서화 •정규표현식 •파서 • 플러그인 • 성능 최적화

    Go에 자신감 덕분에 Go 코딩에 자신감도 많이 붙었어요.
  544. - [한글라이즈 재제작기] 이흥섭(넥슨 왓 스튜디오) 〈한글라이즈〉는 외국어 단어를 외래어

    표기법에 따라 한글로 옮겨 적어주는 도구입니다. "Espresso"를 "에스프레소"로, "東京 "을 "도쿄"로 변환할 수 있죠. 본래 Python으로 구현했던 한글라 이즈를 Go로 재구현하면서 겪은 경험과 느낀점을 공유합니다. Go에 근자감 어떻게 보면 근자감일 수도 있는데 결국 이 발표까지 하게 됐습니다.
  545. - [한글라이즈 재제작기] 이흥섭(넥슨 왓 스튜디오) 〈한글라이즈〉는 외국어 단어를 외래어

    표기법에 따라 한글로 옮겨 적어주는 도구입니다. "Espresso"를 "에스프레소"로, "東京 "을 "도쿄"로 변환할 수 있죠. 본래 Python으로 구현했던 한글라 이즈를 Go로 재구현하면서 겪은 경험과 느낀점을 공유합니다. Go에 근자감 이번 발표를 준비하면서
  546. - [한글라이즈 재제작기] 이흥섭(넥슨 왓 스튜디오) 〈한글라이즈〉는 외국어 단어를 외래어

    표기법에 따라 한글로 옮겨 적어주는 도구입니다. "Espresso"를 "에스프레소"로, "東京 "을 "도쿄"로 변환할 수 있죠. 본래 Python으로 구현했던 한글라 이즈를 Go로 재구현하면서 겪은 경험과 느낀점을 공유합니다. Go에 근자감 명확하게 정리하지 않고 넘어갔던 부분도 명확하게 이해할 수 있었고
  547. - [한글라이즈 재제작기] 이흥섭(넥슨 왓 스튜디오) 〈한글라이즈〉는 외국어 단어를 외래어

    표기법에 따라 한글로 옮겨 적어주는 도구입니다. "Espresso"를 "에스프레소"로, "東京 "을 "도쿄"로 변환할 수 있죠. 본래 Python으로 구현했던 한글라 이즈를 Go로 재구현하면서 겪은 경험과 느낀점을 공유합니다. Go에 근자감 한글라이즈를 더 많이 발전시킬 수 있는 기회가 되기도 했습니다.
  548. - [한글라이즈 재제작기] 이흥섭(넥슨 왓 스튜디오) 〈한글라이즈〉는 외국어 단어를 외래어

    표기법에 따라 한글로 옮겨 적어주는 도구입니다. "Espresso"를 "에스프레소"로, "東京 "을 "도쿄"로 변환할 수 있죠. 본래 Python으로 구현했던 한글라 이즈를 Go로 재구현하면서 겪은 경험과 느낀점을 공유합니다. Go에 근자감 발표 준비하는 동안 한글라이즈 코딩도 평소보다 더 많이 했던 것 같아요.
  549. I GO 요즘은 매일 일과후에 Go로 코딩하는 것을 취미로 삼고

    있는데
  550. I GO Go는 놀이처럼 느껴질 정도로 코딩하기 즐거운 언어였습니다.

  551. I GO 이렇게 Go 프로그래머 분들을 많이 만날 수 있는

    기회가 생겨서 설레고
  552. I GO 여러분과 교류하며 더 많이 배우고 싶습니다.

  553. I GO 좀 더 이야기 나누고 싶은 분은 sub@subl.ee로 이메일

    보내주세요.
  554. 감사합니다! 이 제작물은 아모레퍼시픽의 아리따 돋움, 그리고 서울남산체, 조선일보명조, 스포카한산스,

    Ubuntu Mono를 사용하여 디자인 되었습니다. 제가 준비한 발표는 여기까지입니다.
  555. 감사합니다! 이 제작물은 아모레퍼시픽의 아리따 돋움, 그리고 서울남산체, 조선일보명조, 스포카한산스,

    Ubuntu Mono를 사용하여 디자인 되었습니다. 끝까지 읽어주셔서 감사합니다.
  556. 김영호, 김찬웅, 김향아, 박종성 만든이 도와주신 분 이흥섭