추가공부

Context API 활용하기 + next.js->typescript로

불닭냠냠 2024. 12. 18. 18:09
/components
  ├── AuthContext.js  (Context를 생성하고 Provider 정의)
  ├── LoginButton.js  (Context를 사용하는 컴포넌트)
  ├── UserInfo.js     (Context를 사용하는 또 다른 컴포넌트)
  └── LogoutButton.js (로그아웃 기능을 제공하는 컴포넌트)

 

이런 파일 구조들이 있다면, 코드들은 아래와 같습니다.

//AuthContext.js

import { createContext, useContext, useState } from 'react';

// 1. Context 생성
const AuthContext = createContext();

// 2. Provider 정의
export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);

  // 로그인 함수
  const login = (username) => {
    setUser({ name: username });
  };

  // 로그아웃 함수
  const logout = () => {
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

// 3. Context 값을 쉽게 가져오는 Hook
export function useAuth() {
  return useContext(AuthContext);
}
//LoginButton.js

import { useAuth } from './AuthContext';

export default function LoginButton() {
  const { login } = useAuth();

  return (
    <button onClick={() => login('홍길동')}>
      Login
    </button>
  );
}
//UserInfo.js

import { useAuth } from './AuthContext';

export default function UserInfo() {
  const { user } = useAuth();

  if (!user) {
    return <p>유저정보가 없습니다.</p>;
  }

  return (
    <div>
      <p>{user.name}!</p>
    </div>
  );
}
//LogoutButton.js

import { useAuth } from './AuthContext';

export default function LogoutButton() {
  const { logout } = useAuth();

  return (
    <button onClick={logout}>
      Logout
    </button>
  );
}
// pages/index.js

import LoginButton from '@/components/LoginButton';
import UserInfo from '@/components/UserInfo';
import LogoutButton from '@/components/LogoutButton';

export default function Home() {
  return (
    <div>
      <UserInfo />
      <LoginButton />
      <LogoutButton />
    </div>
  );
}
import { AuthProvider } from '../components/AuthContext';

export default function MyApp({ Component, pageProps }) {
  return (
    <AuthProvider>
      <Component {...pageProps} />
    </AuthProvider>
  );
}

사실 위 코드들은 제가 타입스크립트로 변환하기 위해서 GPT에게 문제를 내달라고 했습니다.

그래서 지금부터 코드는 Next.js를 Typescript로 변환하는 작업을 할 것입니다. 맨 아래에 정답을 남겨놓겠습니다.

바로 아래 코드는 정답이 아니고, 제가 공부한대로 작성하고 GPT에게 검사를 맡을 예정인 코드입니다.

//AuthContext.tsx

import { createContext, useContext, useState, ReactNode } from 'react';

type User = {
	name: string;
}

type AuthContextType = {
  user: User | null;
  login: (username: string) => void;
  logout: () => void;
};

const AuthContext = createContext<AuthContextType>();

interface AuthProviderProps {
	children: ReactNode;
}

export function AuthProvider({ children }: AuthProviderProps) {
  const [user, setUser] = useState<User | null>(null);

  const login = (username: string) => {
    setUser({ name: username });
  };

  const logout = () => {
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export const useAuth = (): AuthContextType => {
  return useContext(AuthContext);
}
import { AuthProvider } from '../components/AuthContext';

export default function MyApp({ Component, pageProps } : AppProps) {
  return (
    <AuthProvider>
      <Component {...pageProps} />
    </AuthProvider>
  );
}

 

사실 vscode로 빨간줄 확인하면서 해야하는데, 무작정 시도해봅니다..ㅎ

결과는...

1. createContext 기본값 처리

createContext<AuthContextType>()에서 기본값을 undefined로 설정해야 합니다. 그렇지 않으면 useContext를 사용할 때 AuthContext가 undefined일 경우 TypeScript에서 오류가 발생할 수 있습니다.

( 수정: 오류는 런타임 오류컴파일 타임 타입 에러가 있습니다.

Provider를 사용하지 않았을 때는 런타임 오류가 일어나는 것이고,

undefined를 작성하지 않았을 때는 undefined일 가능성의 이유로 컴파일 단계에서 타입 오류를 지정합니다.

따라서 이 부분은 컴파일 타입 에러입니다!)

 

수정 필요: createContext에 기본값을 명시적으로 설정해야 합니다.

2. useContext에서 타입 처리

createContext에 기본값을 undefined로 설정하고, useContext를 사용할 때 context가 undefined일 경우를 처리해야 합니다. 그렇지 않으면 컴포넌트 외부에서 AuthContext를 사용할 때 오류가 발생할 수 있습니다.

 

...^^ 정답은 아래

 

import { createContext, useContext, useState, ReactNode } from 'react';

// 1. User 타입 정의
type User = {
  name: string;
}

// 2. AuthContextType 정의
type AuthContextType = {
  user: User | null;
  login: (username: string) => void;
  logout: () => void;
};

// 3. 기본값을 `undefined`로 설정하여 context가 없을 때 오류를 방지
const AuthContext = createContext<AuthContextType | undefined>(undefined);

// 4. AuthProviderProps 정의
interface AuthProviderProps {
  children: ReactNode;
}

// 5. AuthProvider 컴포넌트 정의
export function AuthProvider({ children }: AuthProviderProps) {
  const [user, setUser] = useState<User | null>(null);

  const login = (username: string) => {
    setUser({ name: username });
  };

  const logout = () => {
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export const useAuth = (): AuthContextType => {
  const context = useContext(AuthContext);

  if (!context) {
    throw new Error('useAuth must be used within an AuthProvider');
  }

  return context;
};

 

거의 다 undefined 문제였습니다..ㅎ undefined에 대해서 확실하게 개념 잡고 가야하겠습니다!