본문 바로가기
framework_library/react

React 소스 적용

by 죄니안죄니 2026. 1. 10.
 
 
4

좋다.
이 소스는 지금까지 우리가 정리한 React 사고방식이 거의 그대로 구현된 예제다.
아래에서는 **“이 코드가 실제로 어떻게 살아 움직이는지”**를
렌더링 → 상태 → 라우팅 → 전역/로컬 책임 관점에서 차근차근 해부한다.


0️⃣ 이 App 컴포넌트의 정체부터

먼저 한 문장으로 정의하자.

이 App 컴포넌트는
“인증 상태를 기준으로
전체 앱의 레이아웃과 라우팅을 결정하는 최상위 오케스트레이터”다

  • UI를 많이 그리지 않는다
  • 대신 결정만 한다
    • 로그인인가?
    • 모바일인가?
    • PC인가?
    • 공용 화면인가?
    • 보호된 화면인가?

👉 전형적인 Root / Shell 컴포넌트 역할


1️⃣ 최초 진입 시: 렌더링이 어떻게 시작되는가

① App 함수 실행 (렌더링)

 
export default function App({ isMobile }) { ... }

React는 여기서 그냥 함수 실행을 한다.
아직 DOM ❌, 화면 ❌

이 시점에 실행되는 것:

  • useState 초기화
  • useNavigate() 호출
  • JSX return 분기 계산

2️⃣ 초기 상태 설계 (아주 중요)

🔹 인증 상태의 “진실 원본”

 
const [userInfo, setUserInfo] = useState(() => {
  const stored = localStorage.getItem('userInfo');
  return stored ? JSON.parse(stored) : null;
});
 
const [nickname, setNickname] = useState(() => {
  ...
});

여기서 설계가 아주 좋다.

  • 서버가 아니라
  • localStorage를 1차 진실로 사용

이 의미는 이거다.

“앱 새로고침 시에도
최대한 빠르게 UI를 복구하겠다”

👉 UX 최적화
👉 그리고 나중에 서버로 검증


3️⃣ 인증 여부는 단 하나의 파생 상태로 결정됨

 
const isAuthenticated = !!nickname;

아주 중요한 설계다.

  • userInfo ❌
  • token ❌
  • roles ❌

👉 딱 하나로 통일

이 덕분에:

  • 조건문이 단순해지고
  • 렌더 분기가 명확해진다

4️⃣ 첫 번째 useEffect — 메뉴 로딩

useEffect(() => {
  if (isAuthenticated) {
    api.get('/menus') ...
  } else {
    setMenus([]);
  }
}, [isAuthenticated, isMobile]);

이 effect의 의미

“인증 상태 + 디바이스 타입이 바뀌면
메뉴는 다시 계산되어야 한다”

이건 완벽한 useEffect 사용 예다.

  • 외부 세계(API)
  • 렌더 결과 아님
  • 의존성 명확

👉 우리가 앞에서 말한
“useEffect는 React 바깥과 연결할 때만 쓴다”
그 정석 그대로다.


5️⃣ 두 번째 useEffect — 인증 검증 (핵심)

 
useEffect(() => {
  const token = localStorage.getItem('token')
  if (!token) {
    setNickname('')
    setUserInfo(null)
    return
  }

  // 1. localStorage 즉시 반영
  // 2. 서버로 재검증
}, [])

이 흐름이 아주 중요하다.

순서 정리

  1. 토큰 없음
    • 즉시 로그아웃 상태로 전환
  2. 토큰 있음
    • localStorage 정보로 UI 즉시 렌더
    • /auth/me 호출
    • 성공 → 최신 정보로 갱신
    • 실패 → confirmLogout()

👉 낙관적 렌더링 + 비관적 검증

실무에서 가장 안정적인 패턴이다.


6️⃣ 이제 렌더 분기 로직이 시작된다 (진짜 핵심)

여기부터가 이 App의 “두뇌”다.


🔹 1단계: / 루트 접근

 
if (window.location.pathname === '/') {
  return <Navigate to={isAuthenticated ? "/dashboard" : "/login"} />
}

👉 앱의 단일 진입 규칙


🔹 2단계: 로그인한 사람이 로그인 페이지에 있으면?

 
if (isAuthenticated && isAuthPath) {
  return <Navigate to="/dashboard" />
}

👉 의미 없는 화면 차단


🔹 3단계: 비로그인 상태에서 보호된 경로 접근

if (!isAuthenticated && !isAuthPath) {
  return <Navigate to="/login" />
}

👉 라우터 가드 역할


7️⃣ Public Layout vs Private Layout 분리

🔹 비인증 상태

if (!isAuthenticated) {
  return (
    <div className="app-public">
      <Routes>...</Routes>
    </div>
  );
}

특징:

  • 메뉴 ❌
  • 탭 ❌
  • 헤더 ❌
  • 로그인 / 회원가입만 존재

👉 완전 분리된 레이아웃


8️⃣ 인증 후: 모바일 vs PC 분기

 
if (isMobile)
  return <MobileApp ... />

이게 중요한 이유:

PC / Mobile은
상태는 공유하지만
레이아웃 책임은 완전히 다르다

👉 아주 좋은 분리다.


9️⃣ PC Private Layout의 상태 책임 분해

여기서 지금까지 배운 게 다 나온다.

🔹 App이 소유한 상태들

 
menus        // 앱 전체 네비게이션
openTabs     // 앱 전역 탭 상태
activeId
alert
detail
showLogoutConfirm

이 상태들의 공통점:

  • 여러 하위 컴포넌트가 공유
  • 페이지 전환과 무관
  • 앱 셸의 일부

👉 App이 주인인 게 맞다


🔟 LeftMenu / TopTabs / Content 관계

 
LeftMenu  ── openMenu ─┐
                        ├─ openTabs / activeId
TopTabs   ── onSelect ─┘

👉 상태는 App
👉 행동은 자식
👉 결정은 App

이 구조가 깨끗한 이유:

  • 자식은 상태를 만들지 않는다
  • 자식은 “이벤트만 전달”
  • App이 유일한 조정자

1️⃣1️⃣ logout 흐름은 완벽한 상태 수명 관리 예

function confirmLogout() {
  setOpenTabs([])
  setActiveId(null)
  localStorage.clear()
  setNickname('')
  setUserInfo(null)
  navigate('/login')
}

이게 중요한 이유:

  • UI 상태 초기화
  • 인증 상태 제거
  • 라우팅 이동

👉 상태 생명주기를 한 곳에서 종료

우리가 말한:

“상태는 책임이 끝나는 곳에 둔다”

그 문장의 실전 구현이다.


1️⃣2️⃣ 전체를 한 문장으로 요약하면

이 App은 이렇게 동작한다.

App은
인증 상태를 기준으로
“어떤 레이아웃을 쓸지”를 결정하고,
상태의 주인을 명확히 한 채
자식 컴포넌트는 이벤트만 위임한다


🔚 마지막 정리 (중요)

이 코드가 좋은 이유는:

  • 전역 상태 라이브러리 ❌
  • Context 남용 ❌
  • useEffect 남발 ❌

대신:

  • 상태 위치 명확
  • 책임 분리 명확
  • 렌더링 흐름 예측 가능

👉 지금까지 우리가 이론으로 정리한 React 사고방식이
실제 코드로 정확히 구현된 상태다

'framework_library > react' 카테고리의 다른 글

코드디벨럽2  (0) 2026.01.10
코드 디벨럽  (0) 2026.01.10
Redux는 왜 아직도 쓰이는가  (0) 2026.01.10
그럼 Zustand는 왜 덜 위험한가  (0) 2026.01.10
Context는 왜 상태 관리 도구가 아닌가  (0) 2026.01.10

댓글