인턴
SNS 로그인 로직, 커스텀 훅 하나로 정리하기
jjangsh
2025. 6. 2. 12:18
Next.js를 사용해 로그인 및 계정 연결 UI를 만들다 보면 네이버, 카카오, 애플, 페이스북 등 여러 SNS 로그인 로직이 생기기 마련입니다.
초기에는 각각의 SNS에 맞게 따로따로 작성했지만, 점차 중복이 많아지고 유지보수성이 떨어지는 문제가 생깁니다.
이 포스트에서는 useSnsLogin 커스텀 훅을 통해 중복 로직을 통합한 과정을 소개합니다.
📍 리팩토링 전
1. 로그인 페이지
SNS 종류별로 로그인 함수를 각각 작성해야 했습니다:
const onLoginKakao = async () => { ... };
const onLoginNaver = async () => { ... };
const onLoginApple = async () => { ... };
const onLoginFacebook = async () => { ... };
UI에서 버튼마다 각각의 함수를 연결하는 식으로 사용했습니다:
<Button onClick={onLoginKakao}>카카오로 시작하기</Button>
<IconButton onClick={onLoginFacebook} />
2. 계정 연결 컴포넌트 (SnsStart)
이 페이지도 마찬가지로 각 SNS별 함수가 따로 존재했고,
switch-case 문으로 provider에 따라 분기 처리해야 했습니다:
const onStartAccount = (provider: string) => {
switch (provider) {
case "facebook":
onLoginFacebook(); break;
case "kakao":
onLoginKakao(); break;
// ...
}
};
3. 문제점 요약
- 중복된 axios 요청 로직
- 중복된 try-catch + alert 패턴
- query-string 및 redirectUri 조합 반복
- SNS 추가/변경 시 모든 곳을 수정해야 함
4. 리팩토링 전략: useSnsLogin 훅 만들기
SNS 로그인 로직을 공통화한 커스텀 훅을 만들었습니다.
useSnsLogin.ts
const useSnsLogin = () => {
const { showAlert } = useAlert();
const onLogin = async (provider: string, state?: string) => {
try {
if (provider === "facebook") {
if (!window.FB) return;
window.FB.login((response: any) => {
if (response.authResponse) {
const loginUrl = `${redirectUri}/facebook?openAccessToken=${response.authResponse.accessToken}`;
window.location.href = loginUrl;
}
}, { scope: "public_profile,email", auth_type: "reauthenticate" });
return;
}
const url = qs.stringifyUrl({
url: URL.LOGIN_SOCIAL,
query: {
provider: `ncp_${provider}`,
redirectUri: encodeURIComponent(`${redirectUri}/${provider}`),
reauthenticate: false,
...(state ? { state } : {}),
},
});
const res = await client.get(url);
if (res.status === 200) {
window.open(res.data.loginUrl, "_self");
}
} catch (error: any) {
await showAlert("로그인 실패", breakLine(error.response?.data?.message), {
type: "info",
});
}
};
return { onLogin };
};
📍 리팩토링 후
1. 로그인 페이지
const { onLogin } = useSnsLogin();
const onSnsLogin = useMemo(
() => debounce((provider: string) => onLogin(provider, goFrom), 500, {
leading: true,
trailing: false,
}),
[goFrom, onLogin]
);
버튼 핸들링도 간단하게:
<Button onClick={() => onSnsLogin("kakao")}>카카오로 시작하기</Button>
<IconButton onClick={() => onSnsLogin("apple")} />
2. SNS 계정 연결 (SnsStart)
커스텀 훅 덕분에 switch-case 없이 훨씬 간결하게 바뀌었습니다.
const { onLogin } = useSnsLogin();
const handleClick = () => {
onLogin(account.provider);
};
<Button onClick={handleClick}>
{account.icon}
{account.label}
</Button>
📚 정리
리팩토링 전 리팩토링 후
SNS별로 중복된 함수 존재 | 하나의 onLogin(provider)로 통일 |
에러 처리/alert 로직 반복 | 커스텀 훅 내부에서 일괄 처리 |
UI 코드와 로직 혼재 | UI는 단순 onClick 처리만 담당 |
🧠 마무리
프로젝트 규모가 커지면 로직과 UI를 분리하는 것이 점점 더 중요해집니다.
커스텀 훅은 단순한 함수 분리 이상의 가치를 제공합니다.
공통화, 재사용성, 테스트 용이성까지 모두 얻을 수 있기 때문입니다.
✍️ 다음 목표
- SNS 로딩 상태 처리 및 로딩 스피너 도입
- 로그인 실패 시 UX 개선