Loading...
JavaScript, React, TypeScript 등 학습하며 정리
컴포넌트는 UI, 도메인은 로직, 인프라는 IO
Presentation: 컴포넌트는 상태+렌더만, 데이터 로드는 훅/서비스로
Domain: 토큰 검증/권한 체크 등 비즈니스 규칙은 순수 함수로
Infra: localStorage, fetch, router.push 같은 IO는 한곳에 모으기
단방향: Presentation → Domain → IO (위에서 아래로만 흐름. 아래 레이어는 상위 레이어를 모름)
클릭하여 복사
1// ❌ 안티 패턴: 모든 계층이 뒤섞임
2"use client";
3
4function ProfilePage() {
5 const [user, setUser] = useState(null);
6
7 useEffect(() => {
8 // 1. Infrastructure (localStorage)
9 const token = localStorage.getItem('accessToken');
10
11 // 2. Domain Logic (토큰 검증)
12 if (!token || isTokenExpired(token)) {
13 router.push('/login');
14 return;
15 }
16 // 3. Infrastructure (fetch)
17 fetch('/api/user', {
18 headers: { Authorization: `Bearer ${token}` }
19 })
20 // 4. Presentation Logic
21 .then(res => res.json())
22 .then(data => setUser(data));
23 }, []);
24
25 return <div>{user?.name}</div>;
26}
27
28
29// ✅ 개선된 패턴: 역할별로 분리
30
31// infra (io.ts) : 입출력 IO(input, output) - 밖이랑 소통
32export const getToken = () => localStorage.getItem('accessToken');
33export const fetchMe = (token: string) => fetch('/api/user', { headers: { Authorization: `Bearer ${token}` }});
34
35// domain (auth.ts) : 규칙을 정하는 곳
36export const isValidToken = (token: string) => !isExpired(token);
37
38// hook (useUser.ts) : 훅
39export function useUser() {
40 const token = getToken();
41 const enabled = token && isValidToken(token);
42 return useQuery(['me'], () => fetchMe(token!), { enabled });
43}
44
45// presentation : 보여주고 받아오기
46function ProfilePage() {
47 const { data: user } = useUser();
48 if (!user) return <Spinner />;
49 return <div>{user.name}</div>;
50}