[곰곰 다이어리] Style을 반환해주는 유틸 함수 만들기 (타입스크립트에서 css module import 하기)
곰곰 다이어리는 css module을 쓰고 있다.
답장 리스트를 보는 화면에는 조건에 따라 텍스트의 색이 바뀌어야 한다.
예를 들어,
내가 다이어리의 주인이다 -> 검정색
내가 한 답장이다 -> 분홍색
className={
person._id === correctAnswerer
? Styles.correctAnswerer
: person._id === diaryId
? Styles.notOwner
: Styles.owner
}
이런 식으로 되어 있는데 (사실상 notOwner는 쓰지 않는다;)
person._id에 따라 className을 바꿔주고 있다.
이걸 Style을 뱉어내는 유틸 함수로 바꿔서 JSX에는 함수를 실행하기만 하도록 수정해봤다.
className={getClassNameForAnswerer({
userId: person._id,
correctAnswerer: correctAnswerer,
diaryId: diaryId,
Style: Style,
})}
const getClassNameForAnswerer = (props: IAnswererClassName): string | undefined => {
const { userId, correctAnswerer, diaryId, Style } = props;
if (!correctAnswerer || !diaryId) {
return undefined;
}
if (userId === correctAnswerer) {
return Style.correctAnswerer;
}
return Style.owner;
};
Style의 type은 뭘까?
처음엔 단순히 string이라고 생각했는데 Style에서 타입을 읽지 못해 에러가 떴다.
Style에 호버해보니 Style의 type은 CSSModuleClasses였다.
interface IAnswererClassName {
userId: string;
correctAnswerer: string | undefined;
diaryId: string | undefined;
Style: CSSModuleClasses;
}
생각해보니 vite를 쓰지 않고 리팩토링을 했을 때 타입스크립트가 Style의 타입을 이해하지 못해 에러가 많이 떴었다.
그때 한참 src/global.d.ts에 다음과 같이 적어 타입을 선언해줘야 했었는데,
declare module '*.module.css' {
const content: { [key: string]: string };
export = content;
}
type CSSModuleClasses = {
readonly [key: string]: string;
}
이게 그거였다!
CSS Modules는 CSS 파일의 클래스 이름을 자동으로 고유하게 만들어서 전역적인 이름 충돌을 방지한다.
/* MyComponent.module.scss */
.container {
background-color: #f0f0f0;
}
// MyComponent.tsx
import styles from './MyComponent.module.scss';
export const MyComponent = () => {
// `styles.container`는 고유한 클래스명으로 변환됩니다.
// 예: `MyComponent_container__abc12`
return <div className={styles.container}>...</div>;
};
여기서 import styles from ~ 을 보면 꼭 자바스크립트 파일처럼 CSS 파일을 가져와 객체로 사용하는 걸 알 수 있다.
이 객체는 CSS 파일에 정의된 클래스명(예제에서는 container)를 key로,
자동으로 생성되는 고유한 클래스명 (MyComponent_container__abc12)을 value로 가진다.
그런데 타입스크립트는 이 styles 객체에 어떤 키가 있는지 알지 못하기 때문에 단순히 string으로 접근할 수 없게 된다.
styles는 특정 클래스명들을 key로 가지고 있는 객체기 때문이다.
vite에는 client.d.ts에 자동으로 저 CSSModuleClasses 인터페이스가 선언되어 있다!
제대로 알고 쓰자...