diff --git a/code.js b/code.js new file mode 100644 index 0000000..24acf11 --- /dev/null +++ b/code.js @@ -0,0 +1,112 @@ +// ❌ 안티패턴: 모든 계층이 뒤섞임 +"use client"; + +function ProfilePage() { + const [user, setUser] = useState(null); + + useEffect(() => { + // 1. Infrastructure (localStorage) + const token = localStorage.getItem('accessToken'); + + // 2. Domain Logic (토큰 검증) + if (!token || isTokenExpired(token)) { + router.push('/login'); + return; + } + // 3. Infrastructure (fetch) + fetch('/api/user', { + headers: { Authorization: `Bearer ${token}` } + }) + // 4. Presentation Logic + .then(res => res.json()) + .then(data => setUser(data)); + }, []); + + return
{user?.name}
; +} + + +// ♻️ Refactor + +// 기존에는 ProfilePage 안에서 토큰 확인 + fetch + 리다이렉트를 모두 처리하고있음 + +// services/storage.ts +// 저장소 역할만 담당 +// 토큰을 어디서 읽는지만 담당 + +// 추후에 뭔가 변화가 생긴다면 모든 페이지에서 수정하는 것이 아닌 이곳에서만 수정하면 +// 한번에 해결됨 +export const storage = { + getToken: () => localStorage.getItem('accessToken'), +}; + +// services/api.ts +// 서버와 통신만을 책임짐 +// 백엔드와 통신만을 담당 + +// Pages, Hooks, Components 어디서든 API 로직 재사용 가능 +// 에러 핸들링 일원화 가능 +// 추후에 axios 같은 라이브러리로 변경시 여기에서만 바꿔주면 됨 +export const api = { + getUser: async (token: string) => { + const res = await fetch('/api/user', { + headers: { Authorization: `Bearer ${token}` } + }); + if (!res.ok) throw new Error('Failed to fetch'); + return res.json(); + } +}; + +// hooks/useUser.ts +// 유저 정보를 react-query로 관리 +// 토큰을 기반으로 유저 정보 가져오는 로직만 담당 + +// 이제 페이지에서 fetch 관리 안 해도됨 +import { useQuery } from '@tanstack/react-query'; + +export function useUser() { + const token = storage.getToken(); + + return useQuery({ + queryKey: ['user', token], + queryFn: () => api.getUser(token!), + enabled: !!token && !isTokenExpired(token), + }); +} + +// components/ProtectedRoute.tsx +// 공통 라우트 보호 처리 +// 토큰 체크 후 보호 라우트 처리만 담당 + +// 모든 페이지에 같은 로그인 체크 로직을 넣을 필요가 없음 +// 재사용 가능 +export function ProtectedRoute({ children }) { + const router = useRouter(); + const token = storage.getToken(); + + useEffect(() => { + if (!token || isTokenExpired(token)) { + router.push('/login'); + } + }, [token]); + + if (!token) return null; + return children; +} + +// ProfilePage.tsx +// 페이지 본연의 역할만 담당 +// 렌더만 담당 + +// 읽기 쉬워짐, 유지보수 쉬워짐, 테스트가 쉬워짐 +// 난잡하게 로직이 섞여져있던 형태가 사라짐 +"use client"; +export function ProfilePage() { + const { data: user } = useUser(); + + return ( + +
{user?.name}
+
+ ); +}