toss / es-hangul

A modern JavaScript library for handling Hangul characters.
https://es-hangul.slash.page/
MIT License
1.34k stars 95 forks source link

[Feature]: josa 함수 처리에서 누락된 조사를 추가해주세요. #119

Open RanolP opened 5 months ago

RanolP commented 5 months ago

Description

josa 함수에서 누락된 조사 처리가 있습니다. 아래 목록은 참고를 위해 한국어 단어 데이터베이스 쿼리를 통해 구한 목록이며, 모든 한국어 조사를 포함하지는 않을 수도 있습니다.

목록:

*: 규범 표기가 아님 **: 예스러운 표현

모든 조사를 꼭 지원할 필요는 없지만 몇몇 조사는 종종 쓰이는 경우가 있을 것 같아요. 가령 RPG 웹 게임에서 대사를 만든다든지요.

// 황혼검이야말로 대장장이 지크가 만든 최고 역작이지
// 핏빛 도끼야말로 대장장이 톰슨이 만든 최고 역작이지
show(`${josa(item, '이야말로/야말로')} 대장장이 ${josa(npcSmith.name, '이/가')} 만든 최고 역작이지.`)
const [strong1, strong2] = shuffled(strongEnemies)
// 오우거며 용아병이며 불꽃 도끼를 상대론 이길 수 없었지
show(`${josa(strong1.name, '이며/며')} ${josa(strong2.name, '이며/며')} ${josa(item, '을/를')} 상대론 이길 수 없었지.`)

혹은 조건 나열에도 쓰일 법은 한데, 해당 케이스에는 아마 문장을 통으로 쓸 것 같아서 잘 쓰이진 않을 수도 있겠네요.

const reason: '이미 가입 중' | '만 19세 이하' | ... = ...;
`${josa(reason, '이라서/라서')} 가입할 수 없어요`

Possible Solution

전제 조건

  1. 서술격 조사 이다의 경우 활용형이 너무 많아서 타입으로 잡는 데에 한계가 있고(심지어 코드베이스에 존재하는 '이에요/예요' 처럼 축약형까지 고려하면 일반화가 꽤나 까다롭습니다)
    • 축약형은 고려하지 말아봅시다...
  2. 처리하지 않고 있는 대부분의 조사는 '이'가 생략되는 형태입니다.

해결책

  1. 위 조건을 고려할 때, 아래*와 같은 코드로 처리가 가능할 수 있습니다.

    • 자동 완성이 필요하거나 예외 케이스(예요 등)가 필요하다면 JosaOption에 자주 쓰이는 조사를 미리 등록해두면 해결됩니다.

    아래*

    // Taken from type-fest. CC0-1.0.
    export type IsEqual<A, B> =
      (<G>() => G extends A ? 1 : 2) extends
      (<G>() => G extends B ? 1 : 2)
        ? true
        : false;
    
    type ISkip<T extends string> =
      T extends `이${infer L}/${infer R}`
      ? IsEqual<L, R> extends true
        ? T
        : never
      : never;
    
    type JosaOption =
      | '이/가'
      | '을/를'
      | '은/는'
      | '으로/로'
      | '와/과'
      | '아/야'
      | '이랑/랑'
      | '이에요/예요'
      | '으로서/로서'
      | '으로써/로써'
      | '으로부터/로부터';
    
    declare function josa<T extends string>(word: string, josa: JosaOption | ISkip<T>): string;
    
    josa('돈', '이야말로/야말로')
    josa('돈', '이나/나')
    // @ts-expect-error
    josa('돈', '이야말로/야말')
    // @ts-expect-error
    josa('돈', '이나/이나나')
  2. 특수한 조사 옵션 (이)를 만듭니다.

    • 이 경우 josa(str, '이나/나')josa(str, '(이)') + '나'처럼 쓰게 됩니다.
    • 또는, `(이)${string}` 타입을 허용해서 josa(str, '(이)나')처럼 처리할 수도 있습니다.
      • 런타임 코드에서는 startsWith('(이)')로 분기를 추가하게 되어 약간의 복잡도가 증가합니다.
      • 자동 완성이 필요하거나 예외 케이스(예요 등)가 필요하다면 JosaOption에 자주 쓰이는 조사를 미리 등록해두면 해결됩니다.
    • 개인적으로 작성했던 스크립트에서 해당 방식을 채택했는데, 모든 조사를 나열하지 않고 쉽게 정의해 구현이 편리했었습니다만, 현재 es-hangul의 구현 방식 및 라이브러리 사용자 측 DX를 고려할 때 유리한 점이 없을 수도 있을 것 같습니다.
    • 하위 호환성을 고려할 때 채택이 어려울 수도 있겠습니다.

etc.

No response

RanolP commented 5 months ago

82 와 일부 중복된 논의겠습니다만, 제시된 조사들도 받침 유무 로직으로 처리가 되다보니 타입으로 제약할 필요는 없어 보여요. 타입을 약간 수정해서 다양한 경우에 사용할 수 있도록 바꿀 수 있지 않을까요?

crucifyer commented 4 months ago

동의합니다. josa('돈', '이/') + '야말로' 식으로 하는편이 코드가 단순하고 좋을 것 같습니다.

okinawaa commented 4 months ago

늦게 코맨트 남겨 죄송합니다. 자세하게 남겨주셔서 감사합니다.

제가 이슈의 주제를 잘 이해하지 못했는데요, 누락된 조사가 있다는게 문제라면, 축약형 이외에 조사들을 추가해주면 되지 않나요?

RanolP commented 4 months ago

언급한 이슈에서 결론지어진 방향성이 "그 유용성에 비해 자주 쓰이지 않기에 코드 복잡도 및 유지보수 부담을 줄이기 위해 추가하지 않는다"에 가깝다고 판단했고(제가 비약한 결론이라 아니라면 정정해주세요), 복잡도를 크게 증가시키지 않으면서 다양한 조사를 처리할 방법에 대한 제안이었습니다

crucifyer commented 4 months ago

JosaOption 타입을 없애고 문자열로 다루면 간단해질거라 생각합니다.

JosaOption 을 유지하더라도 '이야말로/야말로' 를 '이/' + '야말로' 로 처리하면 대부분의 패턴은 해결될 것으로 보입니다.

okinawaa commented 3 months ago

josa옵션의 타입을 지정함으로써 josa 함수를 사용하는 타입스크립트 사용자의 자동완성적인 메리트가 너무 강력하여, string type으로 수정하는것은 어려울 것 같아요. ㅠ

타입은 유지하되, 추가가 필요한 조사들은 추가하는게 좋을 것 같습니다. 한번 추가해두면 유지보수에 크게 어려운 부분은 없을 것 같아요. 구현은 이미 되어있기 때문입니다.

규범인 것, 그리고 예스러운 표기가 아닌것 부터 우선 추가해보면 좋을 것 같다고 생각해요!

crucifyer commented 3 months ago

(아까 썼던건 _로조사 에 대한 이해부족으로 삭제했습니다.)

조금 다른 내용이지만, 함께 생각해야 할 것 같아 여기에 적습니다. https://ko.dict.naver.com/#/correct/korean/info?seq=1657 이에요/예요 는 같은말 입니다. 이에요/에요 가 원래의 구별이고, 이에요를 예요로 줄일 수 있는것입니다.

황혼검이에요
핏빛 도끼에요 (예요 x)
영수에요
영숙이이에요 -> 영숙이예요
type JosaOption =
  | '이어/'
  | '이에/'

const 에요_조사: JosaOption[] = ['이어/', '이에/'];

const isEndsWith이 = word[word.length - 1] === '이';
if(isEndsWith && 에요_조사.includes(josa)) {
 대충 이에->예, 이어->여 로 바꾸는 코드
}