Skip to content

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

@RanolP

Description

@RanolP

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions