
declare module은 타입스크립트 문법으로, 특정 module에 선언된 타입을 추가하거나 수정해서 사용하기 위해 사용한다.
다음은 금요일에 작성했던 코드의 일부다.
declare module 'next-auth' {
interface Session {
user: {
id: string
} & DefaultSession['user']
}
}
next-auth의 Session 타입을 커스텀 해야하는 일이 있었다.
기존 Session 타입의 user 속성에 id 속성을 추가해야 했던 것이다.
이를 위해 기존에 선언된 Session 타입을 중복해서 선언함으로써 원래 갖고 있던 속성들을 그대로 가져왔고, user 속성에 id 속성을 추가했다. (interface 타입의 선언 병합 개념)
자세히 보면 user 속성에 DefaultSession['user']를 확장 선언했는데, 그 이유는 user 속성을 커스텀할 경우 기존 user 속성이 전부 날아가기 때문이다.
이로써 원래 갖고 있던 속성을 그대로 가져오면서 id값을 추가할 수 있었다.
결과적으로 Session 타입은 user에 id 속성이 추가되어 다음과 같이 코드에 적용된다.
interface Session {
user?: {
id: string
name?: string | null
email?: string | null
image?: string | null
}
expires: ISODateString
}
문득 '꼭 declare module을 써야할까? 커스텀 타입을 만들어 사용하면 안 될까?'라는 궁금증이 생겼다.
이에 대해 gemini에게 물어보니 declare module을 쓰는 게 더 좋다는 답변을 얻었다.
커스텀 타입을 만든다고 하면 이렇게 만들 수 있을 것이다.
interface MySession extends Session {
user: {
id: string
} & DefaultSession['user']
}
만약 커스텀 타입을 만들어 사용할 경우, useSession() 훅이 반환한 값에서 user의 id 값에 접근하려고 하면 에러가 발생한다.
const { data: session, status } = useSession()
session?.user.id // 에러 발생
const mySession = session as MySession
mySession?.user.id // 정상 작동
이럴 때 타입 캐스팅을 활용해서 MySession 으로 타입을 바꿔줘야 하는데, 만약 session을 사용하는 컴포넌트가 늘어나면 불필요한 타입 캐스팅 코드가 반복된다.
이런 반복을 없애기 위해 declare module을 사용한다.
declare module을 사용하면 next-auth 모듈 내의 타입을 수정하기 때문에 typescript 컴파일러가 이를 인식하고, id값에 접근해도 오류를 내지 않는다.
반복되는 코드를 줄여주고 코드 자동완성 기능까지 제공하는 편리한 기능이다.