오늘은 기존에 JS 코드로 작성해놨던 BoardProject 컴포넌트 파일 전체를 Typescript로 변환하였다.
변환하는 과정에서 실제로 Typescript가 상당히 엄격하구나 라는 생각이 들었고 그렇기 때문에 더더욱 추후에 타입 문제로 인한 오류로 골치 아플 일은 없겠다 생각했다.
우선, Typescript로 변환하는 과정에서 생긴 느낀 점, 혼동이 왔던 부분, 해소되지 않은 궁금증에 대해서 기록하려고 한다.
타입스크립트
는 매우 엄격하고, 예민해서 화를 자주 낸다(에러표시).
1. 지정된 타입과 다른 타입의 데이터를 주면 화를 낸다.
2. 지정된 타입 이외에 다른 타입이 들어올 수도 있는 경우의 수를 제공하면 화를 낸다.(중요!!)
3. JS에서는 유하게 넘어갔지만, Typescript는 얄짤없다! 최대한 명시적으로 작성해줘야한다.
2번의 이유 때문에, try-catch문으로 예외처리를 해줘서 절대 다른 타입의 데이터가 들어가지 않도록 약속해주는 과정이 필요했다.
"!!"
는 불리언 값을 얻기 위해 사용되는 논리 부정 연산자의 두 번의 사용이고, "??"
는 nullish 병합 연산자로서 null 또는 undefined인 값을 처리하기 위해 사용된다.
defaultValue={props.data?.fetchBoard.writer} // 오류 발생
defaultValue={props.data?.fetchBoard.writer ?? ""} // 오류 해결
위 코드를 보면, JS 코드에서는 1번처럼 작성해도 오류가 발생하지 않는다.
만약 데이터가 있다면 해당하는 데이터를 defaultValue값으로 설정한다. 없다면 undefined이므로 "아 없구나.. 아무것도 안할게"하고 defaultValue값을 지정하지 않는다.
그러나, 타입스크립트에서는 달랐다.
defaultValue에는 문자열이 들어가도록 설정이 되어있는데, undefined를 받아올 가능성이 있기 때문에 화를 내는 것이다.
그렇기 때문에, 우리는 친절하게 "그래, 데이터 없으면 그냥 빈 문자열
로 해줘"라고 작성해줘야 하는 것이다.
두 번째 예시로,
readOnly={!!props.data?.fetchBoard.writer} //1번째 방법
readOnly={Boolean(props.data?.fetchBoard.writer)} //2번째 방법
마찬가지로, readOnly는 true 또는 false 즉, boolean 값을 할당해줘야 한다.
JS에서는 융통성있게 "아 문자열이 있네? true로 판단!, 문자열 비어있네? false로 판단!" 하면서 넘어갔겠지만, 타입스크립트에서는 "undefined가 들어오면 어떡하냐!" 하고 화를 낸다.
따라서, 우리는 친절하게 명시적으로 Boolean 값을 만들어줘서 절대 boolean값 이외의 값이 너에게 안갈거야 라는 것을 약속해줘야한다.
같은 구조의 코드인데 작성자는 저렇게 엄격하게 검사하고, 제목의 defaultValue는 undefined를 따로 처리해주지 않았는데도 오류를 표시하지 않는다.
차이점에 대해서 생각을 계속 해봤는데도 아직까지 답을 찾지 못했다ㅠㅠ..
아래의 writer 타입은 없을 수 있는 값이므로 없는 경우(nullish한 경우)를 대비해 빈 문자열("")로 처리해 주고 있다.
반면에, 아래의 title 타입은 반드시 있는 타입으로 nullish한 경우를 굳이 대비해 주지 않고 있다.
따라서, 한 줄 결론은 이렇다, 모든 타입은 다 명시적으로 작성이 되어야 하며, title만 nullish 처리하지 않은 이유는 title을 서버에서 반드시 보내줄 것이라고 명시적으로 나타나 있기 때문이다!
typescript로 변환하기 위해 수많은 type들을 정의해줬다.
함수의 타입을 정의할 때 그 함수가 인자를 필요로 하는 함수라면 반드시 인자까지 작성을 해주어야 했다.
export interface IBoardListUIProps {
data?: Pick<IQuery, "fetchBoard">
isEdit: boolean
onChangeWriter: (event: ChangeEvent<HTMLInputElement>) => void
writerError : string
onChangePassword :(event: ChangeEvent<HTMLInputElement>) => void
passwordError : string
onChangeTitle : (event: ChangeEvent<HTMLInputElement>) => void
titleError : string
onChangeContents : (event: ChangeEvent<HTMLTextAreaElement>) => void
contentsError : string
onClickUpdate : () => void
onClickSubmit : () => void
isActive : boolean
}
위 처럼, onChangeWriter 함수는 event 인자를 받아오고, 그 event 인자의 타입에 대해서도 알려줘야한다. 이 것은 react에서 제공해주는 ChangeEvent, MouseEvent 등을 활용해주면 된다.