안녕하세요.
오늘은 프론트 개발자가 TypeScript로 개발하기 편한 환경을 만들기 위해 한발짝 더 다가가보겠습니다.
최근 TypeScript를 사용하여 백엔드와 통신할 때 데이터 타입 추론이 어려운 경우가 있었습니다.
그래서 백엔드에서 전송된 데이터의 타입과 내가 설정한 타입이 일치하지 않을 때, 콘솔에서 어떤 부분이 다른지 명확하게 표시되도록 작업해주었습니다.
/**
* print zod error log in console
* @param error zod error
*/
export function logZodError<T>(error: z.ZodError<T>) {
error.issues.map((el) => {
if ("received" in el && "expected" in el) {
console.log(
`[${el.path}]: 받은 타입: ${el.received}, 받아야할 타입: ${el.expected}, 메시지: ${el.message}`
);
}
});
}
위 코드는 에러를 정의해주고 , type에 따라 다르면 에러 메시지를 띄워주는 코드입니다.
const { isFiles, isCheckedServer, requestSchema, responseSchema, domain } =
options;
//각각의 변수가 null 또는 undefined가 아닌지를 체크
if (!!requestData && !!requestSchema) {
//주어진 데이터를 스키마에 맞게 안전하게 파싱하여 결과를 반환
const requestParseResult = requestSchema.safeParse(requestData);
if (!requestParseResult.success) {
logZodError(requestParseResult.error);
}
}
위에 코드는 TypeScript를 사용하여 데이터의 유효성을 검사하고 오류를 처리하는 코드입니다.
zod에서 지원해주는 safeParse을 사용해서 true인지 false인지 구별하고
requestParseResult.success가 false일 경우,
즉 데이터가 스키마에 맞지 않는 경우에는 logZodError 함수를 호출하여 에러를 처리합니다.
이후에 클라이언트에서 서버와의 통신 중에 발생할 수 있는 인증 관련 문제를 처리하는 코드를 구현했습니다.
헤더 설정
const headers: HeadersInit = {};
//HTTP 요청 시에 JSON 형식의 데이터를 전송할 때 필요한 설정
if (!isFiles) headers["Content-Type"] = "application/json";
const accessToken = localStorage.getItem("accessToken") || null;
if (accessToken) headers["Authorization"] = `Bearer ${accessToken}`;
응답 데이터 처리
// accessToken, refreshToken 만료 체크 및 갱신 로직
if (responseData) {
if (responseData.code === 300) {
// accessToken 만료 시, refreshToken을 이용해 새로운 accessToken 요청
const getAccessToken = await fetch(url, {
body: JSON.stringify({
refreshToken: localStorage.getItem("refreshToken"),
}),
});
const getAccessTokenData = await getAccessToken
.json()
.catch(() => null);
if (getAccessTokenData.code === 100) {
// accessToken과 refreshToken 재발급 성공 시
localStorage.setItem(
"accessToken",
getAccessTokenData.data.accessToken
);
localStorage.setItem(
"refreshToken",
getAccessTokenData.data.refreshToken
);
if (!fetchOptions?.isRefetch) {
// 최초 재요청 시 fetchWithZod 함수를 이용해 요청 재시도
return fetchWithZod(method, path, requestData, {
...fetchOptions,
isRefetch: true,
});
} else {
// 2회 이상의 재요청인 경우 에러 메시지 반환
return { code: 0, message: "2번 이상의 재요청" };
}
} else if (getAccessTokenData.code === 301) {
// refreshToken 만료 시, 로컬 저장소에서 accessToken과 refreshToken 제거 후 로그아웃 처리
localStorage.removeItem("accessToken");
localStorage.removeItem("refreshToken");
return signOut({ callbackUrl: "/login" });
}
}
}
위 코드는 보안 및 사용자 경험을 개선하기 위해, 인증 토큰의 유효성을 검사하고
만료된 경우 자동으로 갱신하는 방식으로 설계되었습니다.
이는 클라이언트가 항상 유효한 인증 토큰을 유지하며, 사용자에게 중단 없는 서비스 이용을 제공하기 위한 것입니다.
프로젝트에서 적용하면서 느낀점
- 프로젝트에서는 이러한 방식으로 클라이언트와 서버 간의 통신에서 필요한 헤더를 TypeScript를 통해 안전하게 설정할 수 있습니다. 이는 런타임 에러를 줄이고 실수를 미리 방지하는 데 도움이 되었습니다.
- 특히 인증과 관련된 코드는 보안적 측면에서 중요하므로, 토큰 관리와 갱신 로직을 TypeScript로 타입 안정성 있게 처리함으로써 보안 취약점을 줄이고 안전성을 높일 수 있습니다.
- TypeScript는 코드의 타입 정의를 명확하게 해주므로, 헤더 설정이나 인증 관련 문제가 발생했을 때 코드를 더 쉽게 디버깅할 수 있습니다. 예를 들어, 백엔드에서 예상하지 못한 데이터 구조를 받았을 때 TypeScript는 이를 타입 불일치로 감지하여 경고를 주기때문에 수정하는 시간을 줄일 수 있었습니다.
'TIL' 카테고리의 다른 글
설계 단계_아키텍처 설계 (0) | 2024.05.10 |
---|---|
React Query (0) | 2023.11.13 |
[React] React에서 모달을 활용한 날짜와 시간 선택 관리하기 (0) | 2023.11.12 |
마크다운 사용 (0) | 2023.11.10 |
UTC와 KST (0) | 2023.11.10 |