Front-end) 쇼핑몰 프로젝트 홈 화면 설계
이전 포스터 Front-end) 프로젝트 GraphQL 연동과 커스텀 Hook이전 포스터 Front-end) 프로젝트 아토믹 디자인 패턴 적용이전 포스트 Front-end) 프로젝트 설계 및 기초 세팅쇼핑몰 기능 구상1. 메인 화면He
dmltn3426.tistory.com
✅Intro
- 로그인 로직 리펙토링 이전 문제점
- 로그인 로직에서의 개선방향 생각하기
- 리펙토링 이후에 개선된 점
- 소셜 로그인을 진행하면서 생겼던 문제점
- 네이버 로그인과 카카오 로그인 통합 소셜 로그인 개발
로그인 로직 리펙토링 이전 문제점이 무엇이였는지?
1. Template, Page에서 서비스 로직처리를 하지 않음
아토믹 디자인 구조를 짜게 되면 보통적으로 Template나 Page 부분에서 대부분의 서비스로직을 짜야 하는것으로 알고 있었다. 하지만 그전에 로그인 화면은 서비스 로직을 제대로 처리하지 못해서 계속해서 TextInput에 데이터가 바뀔 때 마다 계속해서 리렌더링이 발생하면서 서버에 재요청을 보내게 된다. 그렇게 되면 gql쿼리를 계속해서 재실행 하는 현상이 발생하게 되고 아래 gif를 통해 확인할 수 있다.

2. 불필요한 state를 관리하는 변수와 함수 존재
- 기존에는 accessToken, refreshToken과 같은 jwt 토큰 구조를 이용해서 인증하는 방식으로 쿠키에 저장하는 방식으로 resposne로 데이터를 저장하고 쿠키에 우리가 직접적으로 recoil이나 redux로 관리하는 방식으로 진행하였다.
- jwt 토큰이 header로 넘겨주는것이 아닌 resposne나 request로 넘겨주는 것은 보안상 위험한 방식이라는 말이 나오면서 Back-end와 협의 후에 Back단에서 직접적으로 쿠키를 통해 우리에게 Authorization토큰을 넘겨주는 가져가는 방식을 채택하였다.
- accessToken, refreshToken의 state를 관리하는 변수와 쿠키를 저장하는 함수들이 코드적으로 필요가 없어져 문제가 발생하였다.
결론
첫번째 문제는 organism에 있는 서비스 로직을 template로 옮겨 organism에 있는 TextInput이 변경 되면 organism에 있는 컴포넌트만 리렌더링 되기 때문에 template으로 옮겨진 서버를 호출시키는 함수는 버튼을 누를 때만 작동할 것이다.
두번째 문제는 Front-end에서는 accessToken을 저장하는 부분이 딱히 필요가 없어졌기 때문에 그에 대한 함수와 변수를 불필요하게 관리 할 필요가 없어져 삭제해주기로 하였다.
로그인 로직 리펙토링 개선된 이후에 상황
변경 된 코드
const isInMobile = useDomSizeCheckHook(768);
const navigate = useNavigate();
const [loginId, setLoginId] = useState<{ pwd: string; userId: string; sns: string }>({
pwd: '',
userId: '',
sns: 'NORMAL',
});
// 처음 랜더링 때 로그인 여부 판단하는거 잡기 위해서
const [isFirstRender, setIsFirstRender] = useState(true);
// 로그인 되어 있는지 판단하는 상태 관리 변수
const [isLoggedIn, setIsLoggedIn] = useState(false);
// 유저 계정 상태를 전역으로 관리하는 변수
const [account, setAccount] = useRecoilState(accountAtom);
// 로그인 GraphQL 쿼리 문
const { refetch: login } = useGraphQL({
query: LOGIN_REQUEST,
type: 'mutation',
request: { ...loginId },
option: { 'Authorization-mac': 맥주소 },
});
// 유저 계정 데이터 정보 받아오는 GraphQL 쿼리 문
const { data: user, refetch: userRefetch } = useGraphQL({
query: ACCOUNT,
type: 'query',
});
// 회원 가입으로 이동하는 signUp navigation
const handleSignUp = () => {
navigate('/signup');
};
/* data에 pwd, userId, sns를 props 속성 값으로 받아서 loginId 데이터 변경 */
const handleFormSubmit = useCallback((data: { pwd: string; userId: string; sns: string }) => {
setLoginId(data); // loginId를 업데이트
}, []);
/* 첫 렌더링 이후에 login 호출 후에 저장 된 쿠키를 서버로 부터 받는다.
* 의존성 배열에 loginId를 추가하여 useCallback과 연결된 handleFormSubmit 함수가 실행 되어 loginId값이 업데이트 될 때 만
* useEffect가 실행되게 구조를 만들어 변화가 일어날 때마다 렌더링 되는것을 방지했다. */
useEffect(() => {
// 첫 렌더링 이후에만 login 호출
if (!isFirstRender) {
login()
.then(() => {
setIsLoggedIn(true);
})
.catch((err) => {
console.error('로그인 실패:', err);
setIsLoggedIn(false);
});
} else {
setIsFirstRender(false);
}
}, [loginId]); // loginId가 변경된 후에 login 실행
// 의존성 배열에 isLoggedIn을 추가하여 login이 되었을 때만 user계정을 스키마를 서버로 호출하게 끔 한다.
useEffect(() => {
if (isLoggedIn) {
userRefetch().then((response)=>{
setAccount(response.data);
}).catch((err)=>{
console.log(err);
});
}
}, [isLoggedIn]);
// user 데이터 값이 업데이트 될 때마다 user 계정 값이 업데이트 되게 끔 구현 해뒀다.
useEffect(() => {
if (account != null) {
console.log(account);
navigate('/');
}
}, [account]);
결과 화면

위에 코드와 결과 화면을 확인해 보면 개선되기 전 화면과 다른 화면이 보여주는 것을 확인 할 수 있고 프론트에서 직접적으로 쿠키를 넣어주는 것이 아닌 바로 서버에서 데이터를 받아오는 것을 확인 할 수 있다.
개선된 코드에서를 확인해 보면 좀 더 불필요한 코드를 줄이고 서버에 요청 과부화를 덜 일으키면서 프론트에서도 메모리를 덜 잡아 먹을 수 있는 결과를 얻을 수 있었다.
❓소셜 로그인 로직을 구현하면서 생겼던 문제점
우리가 대표적으로 사용하는 소셜 로그인은 네이버, 카카오, 구글 등이 있을 수 있다고 생각한다. 지금까지 네이버와 카카오를 중심으로 구현을 진행하였고 추후에 구글도 구현 할 예정이다.
어려웠던 점
네이버와 카카오 로그인은 기본적인 보안과 개인정보를 수집하는 부분에서 필요로 하는 비즈니스 권한을 신청하기 위해서는 Front-end 배포를 통해 웹사이트가 배포가 되어 있어야 신청을 할 수 있었다. 그리고 카카오와 네이버 둘 소셜 로그인 다 Redirect URI를 설정해줘서 네이버, 카카오 로그인 창을 띄워 인가 코드를 전달받아 그걸 서버에 전달하는 방식을 채택하므로써 기존에 카카오 자체 로그인을 하는 방식이 아닌 우리가 카카오 로그인을 통해서 이메일 정보와 개인정보를 같이 가져와야 된다는 리스크도 동반되기 때문에 보안관리도 중요해졌다는 부분에서 많이 어려웠던거 같다.
재활용 컴포넌트
- 각 소셜 로그인을 하면 프론트가 인가코드를 서버에게 전달하는 보통적인 소셜 로그인 방식이기 때문에 이 방식을 통합해서 컴포넌트가 작동하도록 재활용 컴포넌트를 만들었다.
- 재활용 컴포넌트를 만들면서 고려 했던 점은 서버에서 인가 코드를 통해 회원가입 된 유저인지 아닌지 비교하고 서버가 회원가입이 되지 않은 유저는 snsToken이라는 토큰을 이용해서 회원가입 하도록 설계하였다.
결론
설계하는 과정에서 많은 어려운 부분을 느낄 수도 있었지만 이것을 통해서 보안에 대해 더욱 깊게 공부 할 수 있었으며 Front,Back - end 둘 다 이중으로 보안을 해야 안전한 프로젝트를 만들 수 있다고 생각할 수 있었다.
코드
const navigate = useNavigate();
const [socialCode, setSocialCode] = useRecoilState(soicalCodeState);
const [accessToken, setAccessToken] = useState<string>('');
const { refetch: socialLogin } = useGraphQL({
query: LOGIN_REQUEST,
type: 'mutation',
request: {
snsCode: '',
sns: '',
},
option: {
'Authorization-mac': 맥 주소,
...(accessToken ? { 'Authorization-SNS': `${accessToken}` } : {}),
},
});
useEffect(() => {
const codeData: string | null = new URL(window.location.href).searchParams.get('code');
if (codeData != null) {
setSocialCode(codeData!);
}
}, []);
useEffect(() => {
if (socialCode) {
socialLogin({
variables: {
request: {
snsCode: socialCode,
sns: social === 'naver' ? 'NAVER' : 'KAKAO',
},
},
})
.then((response) => {
console.log('토큰 발급 성공:', response);
})
.catch((error) => {
if (error.message === '가입되지 않은 회원입니다.') {
alert('회원가입 페이지로 이동합니다.');
navigate('/signup', { state: { sns: social === 'naver' ? 'NAVER' : 'KAKAO' } });
setAccessToken(getCookie('snsToken'));
} else {
console.error('다른 오류 발생:', error.message);
}
});
}
}, [socialCode]);
실행 화면

✅결론
로그인 화면을 설계하고 구현하면서 위의 문제점과 직면도 하게 되었고 jwt 토큰에 대한 공부를 할 수 있었던 기회가 될 수 있었던거 같다.
다음 프로젝트를 진행할 때에는 구글 로그인도 포함하여 더욱 다양한 컴포넌트와 미흡했던 보안쪽 부분을 공부하여 Front-end 부분을 더욱 다듬어 프로젝트를 구성 할 수 있을 수 있다고 생각했다.
참고
출처 : jwt token 관련 문서
JWT.IO
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
jwt.io
출처 : 카카오 로그인 문서
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com
출처 : 네이버 로그인 문서
네이버 로그인 개발가이드 - LOGIN
네이버 로그인 개발가이드 1. 개요 4,200만 네이버 회원을 여러분의 사용자로! 네이버 회원이라면, 여러분의 사이트를 간편하게 이용할 수 있습니다. 전 국민 모두가 가지고 있는 네이버 아이디
developers.naver.com