๐ŸŽฏ ๋ฆฌ์•กํŠธ ๋ผ์šฐํ„ฐ(React Router)๋ฅผ ํ†ตํ•ด์„œ ๊ฒฝ๋กœ๋ฅผ ์„ค์ •ํ•˜๊ณ , API ํ†ต์‹ ๊ณผ ๋ฐ์ดํ„ฐ ๋ ˆ์ด์–ด๋ฅผ ํ†ตํ•ด ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€๋ฅผ ์ œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ“— Today I Learned

React Router

React Router๋Š” ํ•œ ์žฅ์˜ ์›นํŽ˜์ด์ง€ ์•ˆ( SPA )์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋™ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ™”๋ฉด๋งŒ ๋ฐ”๊ฟ”์ฃผ๋Š”๋ผ์šฐํŒ… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.


๐Ÿค” ์™œ React Router๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฑธ๊นŒ?

React๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ SPA ๊ตฌ์กฐ๋ผ ํŽ˜์ด์ง€๋ฅผ ์ด๋™ํ•˜๋”๋ผ๋„ ์ „์ฒด ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ URL์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ํ™”๋ฉด์„ ๋ณด์—ฌ์ฃผ๋Š” URL ๋ผ์šฐํŒ…์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด๋‚˜ SEO, ๊ณต์œ  ๊ธฐ๋Šฅ์—์„œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ผ์šฐํŒ… ๊ธฐ๋Šฅ์„ ์ง€์›ํ•ด์ฃผ๋Š” ๊ฒƒ์ด ๐Ÿ“"React Router" ์ž…๋‹ˆ๋‹ค.


React Router ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜

์ด๋ฏธ ๋ฆฌ์•กํŠธ ํ…œํ”Œ๋ฆฟ์„ ๋งŒ๋“  ์ƒํƒœ์ด๋ฏ€๋กœ ๋‘๋ฒˆ์งธ ๋ฐฉ๋ฒ•์œผ๋กœ ์„ค์น˜ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

1๏ธโƒฃ ๊ณต์‹๋ฌธ์„œ ์„ค์น˜ ๋ฐฉ๋ฒ•( React ํ…œํ”Œ๋ฆฟ์— router ํฌํ•จ๋œ ๋ฐฉ๋ฒ• )

npx create-react-router@latest my-react-router-app

2๏ธโƒฃ React Router DOM ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋‹จ๋… ์„ค์น˜ ๋ฐฉ๋ฒ•( react-router-dom / @types/react-router-dom )

npm install react-router-dom

npm install --save-dev @types/react-router-dom

๐Ÿค” react-router / react-router-dom / react-router-native์˜ ์ฐจ์ด๊ฐ€ ๋ญ˜๊นŒ?

ํŒจํ‚ค์ง€ ์ด๋ฆ„์„ค๋ช…
react-router๋ผ์šฐํ„ฐ์˜ ๊ธฐ๋ณธ ํ•ต์‹ฌ (๋ผ์šฐํŒ… ๋กœ์ง๋งŒ ์žˆ์Œ)
react-router-dom๋ธŒ๋ผ์šฐ์ €์šฉ DOM ๋ผ์šฐํ„ฐ (์›น)
react-router-nativeReact Native ์ „์šฉ ๋ผ์šฐํ„ฐ (๋ชจ๋ฐ”์ผ)



API ์š”์ฒญ ํ”Œ๋กœ์šฐ ์ „์ฒด ๊ตฌ์กฐ

์‚ฌ์šฉ์ž ํ™”๋ฉด์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๊ณ  ๊ด€๋ฆฌํ•˜์—ฌ, ์ตœ์ข…์ ์œผ๋กœ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ์ „์ฒด ํ๋ฆ„ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ [1] View ๐Ÿ‘€  โ”‚
โ”‚ Header        โ”‚  โ† ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ง€๋Š” ํ™”๋ฉด
โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
     โ”‚ ํ™”๋ฉด์ด ๋œฐ ๋•Œ ๋ฐ์ดํ„ฐ ํ•„์š”!
     โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ [2] Hook ๐Ÿ”—   โ”‚
โ”‚ useCategory() โ”‚  โ† ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๋Š” ์ปค์Šคํ…€ ํ›…
โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
     โ”‚ ๋‚ด๋ถ€์—์„œ Query ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ˜ธ์ถœ!
     โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚[3] Query Library โš™๏ธโ”‚
โ”‚ useQuery           โ”‚ โ† ์บ์‹ฑ/๋กœ๋”ฉ/์—๋Ÿฌ ๊ด€๋ฆฌ ๋„์šฐ๋ฏธ
โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
     โ”‚ Fetcher ํ˜ธ์ถœ (์ง„์งœ ์š”์ฒญํ•จ์ˆ˜)
     โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ [4] Fetcher ๐Ÿ“ฌ โ”‚
โ”‚ fetchCategory()โ”‚ โ† fetch๋กœ API ํ˜ธ์ถœ
โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
     โ”‚ ๋ฐฑ์—”๋“œํ•œํ…Œ ๋ฐ์ดํ„ฐ ์š”์ฒญ!
     โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ [5] API Server ๐Ÿง  โ”‚
โ”‚ '/categories'      โ”‚ โ† ์‹ค์ œ ๋ฐ์ดํ„ฐ ์‘๋‹ต (JSON)
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜



react hook form

React์—์„œ ํผ์„ ์‰ฝ๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

์„ค์น˜ ๋ฐฉ๋ฒ•

npm install react-hook-form

์˜ˆ์ œ ์ฝ”๋“œ

import { useForm } from 'react-hook-form';

function SimpleForm() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm();

  const onSubmit = (data) => {
    console.log(data); // { email: '...', password: '...' }
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        placeholder="์ด๋ฉ”์ผ"
        {...register('email', { required: true })}
      />
      {errors.email && <span>์ด๋ฉ”์ผ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”</span>}

      <input
        placeholder="๋น„๋ฐ€๋ฒˆํ˜ธ"
        type="password"
        {...register('password', { required: true })}
      />
      {errors.password && <span>๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”</span>}

      <button type="submit">์ œ์ถœ</button>
    </form>
  );
}
  • register : input์„ ํผ์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. (์ž…๋ ฅ๊ฐ’ ์ถ”์  + ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ํฌํ•จ)

  • handleSubmit : ํผ ์ œ์ถœ ์‹œ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š” ํ•จ์ˆ˜๋กœ ๋‚ด๋ถ€์ ์œผ๋กœ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•˜๋ฉด onSubmit์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

  • formState.errors : ๊ฐ input์˜ ์—๋Ÿฌ ์ƒํƒœ๋ฅผ ๊ฐ์ฒด๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

  • {...register('email', { required: true })}

    • 'email'์ด๋ผ๋Š” key ๊ฐ’์„ ์ถ”์ ํ•ด์คŒ โ†’ ๋‚˜์ค‘์— onSubmit(data)์—์„œ data.email๋กœ ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค.



axios

ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ์„œ๋ฒ„๋กœ HTTP ์š”์ฒญ์„ ์‰ฝ๊ฒŒ ๋ณด๋‚ด๊ณ  ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. fetch API์˜ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, ๋น„๋™๊ธฐ ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๋Š” ๋ฐ ํŽธ๋ฆฌํ•œ ์žฅ์ ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์„ค์น˜ ๋ฐฉ๋ฒ•

npm install axios

์˜ˆ์ œ ์ฝ”๋“œ

//http.ts
import axios, { AxiosRequestConfig } from 'axios';
import { getToken, removeToken } from '../store/authStore';

const BASE_URL = 'http://localhost:9999';
const DEFAULT_TIMEOUT = 30000;

export const createClient = (config?: AxiosRequestConfig) => {
  const axiosInstance = axios.create({
    baseURL: BASE_URL,        // ๊ธฐ๋ณธ ์ฃผ์†Œ
    timeout: DEFAULT_TIMEOUT, // ์š”์ฒญ ํƒ€์ž„์•„์›ƒ
    headers: {
      'Content-Type': 'application/json',
      Authorization: getToken() ? getToken() : '',
    },
    withCredentials: true,    // ์ฟ ํ‚ค ํฌํ•จ ์—ฌ๋ถ€ 
    ...config,                // ์™ธ๋ถ€ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ํ—ˆ์šฉ
  });

  axiosInstance.interceptors.request.use((config) => {
    const token = getToken();
    if (token) {
      config.headers.Authorization = `${token}`;
    }
    return config;
  });

  axiosInstance.interceptors.response.use(
    (response) => response,
    (error) => {
      if (error.response.status === 401) {
        removeToken();
        window.location.href = '/login';
        return;
      }
      return Promise.reject(error);
    }
  );

  return axiosInstance;
};

export const httpClient = createClient();
  • BASE_URL : ๊ธฐ๋ณธ ์š”์ฒญ URL์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

  • DEFAULT_TIMEOUT : ์š”์ฒญ์„ ๊ธฐ๋‹ค๋ฆด ์ˆ˜ ์žˆ๋Š” ์ตœ๋Œ€ ์‹œ๊ฐ„(ms)์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์š”์ฒญ์ด 30์ดˆ ์•ˆ์— ์•ˆ ๋๋‚˜๋ฉด timeout ๋ฉ๋‹ˆ๋‹ค. ๋ฌดํ•œ ๋กœ๋”ฉ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

  • axios.create()๋ฅผ ํ†ตํ•ด ์ปค์Šคํ…€ ํด๋ผ์ด์–ธํŠธ ๊ฐ์ฒด(axiosInstance)๋ฅผ ์ƒ์„ฑํ•ด์ค๋‹ˆ๋‹ค.

    • headers: { 'Content-Type': 'application/json' } : JSON ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›๊ฒ ๋‹ค๊ณ  ๋ช…์‹œํ•ด์ค๋‹ˆ๋‹ค.

    • Authorization: getToken() ? getToken() : '' : ๋กœ๊ทธ์ธํ–ˆ์œผ๋ฉด ํ† ํฐ์„ ๋„ฃ๊ณ , ์—†์œผ๋ฉด ๋นˆ ๋ฌธ์ž์—ด๋กœ ๋‚ด๋ณด๋ƒ…๋‹ˆ๋‹ค.

    • withCredentials: true : ์ด ์„ค์ •์ด ์žˆ์œผ๋ฉด, ์ฟ ํ‚ค ๊ฐ™์€ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋„ ํ•จ๊ป˜ ๋ณด๋‚ด์ค๋‹ˆ๋‹ค.

    • ์™ธ๋ถ€์—์„œ ์ถ”๊ฐ€ ์„ค์ •์„ ๋„˜๊ธฐ๋ฉด ...config๋กœ ๋ณ‘ํ•ฉํ•ด์ค๋‹ˆ๋‹ค.

  • axiosInstance.interceptors.request.use() : ์š”์ฒญ ์ธํ„ฐ์…‰ํ„ฐ๋กœ, ๋งค ์š”์ฒญ ์ „์— ํ† ํฐ์ด ์žˆ๋Š”์ง€ ์ฒดํฌํ•˜๊ณ , ์žˆ๋‹ค๋ฉด Authorization ํ—ค๋”์— ๋ถ™์—ฌ์ค๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธ ์ƒํƒœ ๋ณ€ํ™”์— ๋”ฐ๋ฅธ ํ† ํฐ ๋™๊ธฐํ™”๊ฐ€ ํ™•์‹คํ•ด์ง‘๋‹ˆ๋‹ค.

  • axiosInstance.interceptors.response.use() : ์‘๋‹ต ์ธํ„ฐ์…‰ํ„ฐ๋กœ, ์‘๋‹ต ์ฒ˜๋ฆฌ ์ค‘ ์—๋Ÿฌ๊ฐ€ 401 Unauthorized๋ฉด ํ† ํฐ์„ ์‚ญ์ œํ•˜๊ณ , /login์œผ๋กœ ๋ณด๋ƒ…๋‹ˆ๋‹ค.


์‚ฌ์šฉ ๋ฐฉ๋ฒ•

// category.api.ts
import { Category } from '../models/category.model';
import { httpClient } from './http';

export const fetchCategory = async () => {
  const response = await httpClient.get<Category[]>('/categories');
  return response.data;
};

// cart.api.ts
interface AddCartParams {
  bookId: number;
  quantity: number;
}

export const addCart = async (params: AddCartParams) => {
  const response = await httpClient.post(`/carts`, params);
  return response.data;
};

/categories ๊ฒฝ๋กœ๋กœ GET ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

  • httpClient๋Š” ์„ค์ •ํ•œ axiosInstance๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.

  • response.data๋Š” Axios๊ฐ€ ์‘๋‹ต์„ ๋ฐ›์„ ๋•Œ ์ž๋™์œผ๋กœ .data์— ์‘๋‹ต ๋ณธ๋ฌธ์„ ๋‹ด์•„์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ๋ฆฌํ„ดํ•ด์ค๋‹ˆ๋‹ค.


/carts ๊ฒฝ๋กœ๋กœ POST ์š”์ฒญ์„ ๋ณด๋‚ด์„œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์ฑ…์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

  • params๋Š” { bookId, quantity } ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.

    • ๊ฐ์ฒด๋ฅผ HTTP ์š”์ฒญ์˜ body์— ๋‹ด์•„์„œ ๋ณด๋ƒ…๋‹ˆ๋‹ค.



๋ฐฑ์—”๋“œ ์ฝ”๋“œ ์—ฐ๊ฒฐ

๐Ÿค” ๊ธฐ์กด์— ์ž‘์„ฑํ–ˆ๋˜ ๋ฐฑ์—”๋“œ ์ฝ”๋“œ๋ฅผ ์–ด๋–ป๊ฒŒ ๋ถˆ๋Ÿฌ์™€์•ผ ํ•˜์ง€?

์„œ๋ฒ„์˜ ๊ฐœ๋…์ด ํ—ท๊ฐˆ๋ ค ํ”„๋กœ์ ํŠธ ๋‚ด๋ถ€์— ์žˆ๊ฑฐ๋‚˜ ์„œ๋ฒ„ ์ฝ”๋“œ๋ฅผ ๋‚ด ์ปดํ“จํ„ฐ๋กœ ๊ฐ€์ ธ์™€์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์ง€๋งŒ, ๊ตณ์ด ๊ทธ๋Ÿด ํ•„์š” ์—†์ด ์ฃผ์†Œ(URL)๋งŒ ์•Œ๊ณ  ์„œ๋ฒ„๊ฐ€ ์ž˜ ๊ตฌ๋™๋˜์–ด ์žˆ๋‹ค๋ฉด ์–ด๋””์„œ๋“  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.


CORS(Cross-Origin Resource Sharing)

CORS๋Š” ๋‹ค๋ฅธ ์ถœ์ฒ˜(origin)์— ์žˆ๋Š” ์„œ๋ฒ„์— ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์š”์ฒญํ•  ๋•Œ, ๋ณด์•ˆ์„ ์œ„ํ•ด ๋ง‰์•„๋‘๋Š” ๊ฒƒ(SOP : Same-Origin Policy)์„ ์„œ๋ฒ„๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ํ—ˆ์šฉํ•ด์„œ ํ†ต์‹ ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

  • ์ถœ์ฒ˜(origin): ํ”„๋กœํ† ์ฝœ(Protocol) + ํ˜ธ์ŠคํŠธ(Host) + ํฌํŠธ ๋ฒˆํ˜ธ(Port number)

์ถœ์ฒ˜: CORS(๊ต์ฐจ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ ) / tosspayments


cors ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

ํ˜„์žฌ ํ”„๋ก ํŠธ์™€ ๋ฐฑ์—”๋“œ์˜ ํฌํŠธ ๋ฒˆํ˜ธ๊ฐ€ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— cors ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด CORS ์„ค์ •์„ ํ•ด์ค๋‹ˆ๋‹ค.

const cors = require('cors');
app.use(
  cors({
    origin: true, // ๋ฐฐํฌ ์‹œ์—๋Š” ๋„๋ฉ”์ธ์„ ์ž…๋ ฅํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์ข‹์Œ
    credentials: true,
  })
);
  • origin: true : ์š”์ฒญ์ด ์˜จ ๋„๋ฉ”์ธ์ด ๋ฌด์—‡์ด๋“  ํ—ˆ์šฉํ•ด์ค๋‹ˆ๋‹ค.

  • credentials: true : ์š”์ฒญ์— ์ฟ ํ‚ค๋‚˜ ์ธ์ฆ ์ •๋ณด๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ์˜ต์…˜์ž…๋‹ˆ๋‹ค.

โœจ ํ”„๋ก ํŠธ์—์„œ๋„ axios๋‚˜ fetch์— withCredentials: true๋ฅผ ๋„ฃ์–ด์ค˜์•ผ ๊ฐ™์ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.




โœ๏ธ ํšŒ๊ณ 

์„œ๋ฒ„๋Š” ์ผœ์ ธ์žˆ๋‹ค๋ฉด ์–ด๋””์„œ๋“  ์—ด๋ฆฐ๋‹ค๋Š” ๊ฑธ ๊ฐœ๋…์ ์œผ๋กœ๋งŒ ์ดํ•ดํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€ ๋ง‰์ƒ ํ•œ ํด๋”์—์„œ ์ฝ”๋“œ๋งŒ ์ ๋‹ค๋ณด๋‹ˆ ํ”„๋กœ์ ํŠธ ํŒŒ์ผ๋กœ ๋ฐฑ์—”๋“œ ์ฝ”๋“œ๊นŒ์ง€ ๋‹ค ์˜ฎ๊ฒจ์™€ ๋ฒ„๋ฆด ๋ป” ํ–ˆ๋‹ค..๐Ÿ˜… ํŒ€์›๋“ค์˜ ๋„์›€๋„ ๋ฐ›๊ณ , ๊ฐœ๋… ์ •๋ฆฌ๋ฅผ ํ•˜๋‹ˆ ํ™•์‹คํžˆ ์„œ๋ฒ„์˜ ๊ฐœ๋…์— ๋Œ€ํ•ด ํ™•์‹คํžˆ ์ •๋ฆฝํ•œ ๊ฒƒ ๊ฐ™๋‹ค.

profile
๐ŸŒฑ๊ฐœ๋ฐœ ๊ธฐ๋ก์žฅ

0๊ฐœ์˜ ๋Œ“๊ธ€