Team Project - Nike Clone Coding (4)

peaceminusoneยท2022๋…„ 2์›” 5์ผ
0

[Project] Team Project | Nike Clone Coding

๋ชฉ๋ก ๋ณด๊ธฐ
4/4

๐Ÿ“ฒ ํŒ€ ํ”„๋กœ์ ํŠธ

์ €๋ฒˆ Wepleshop 1์ฐจ ํ”„๋กœ์ ํŠธ๋ฅผ ์ˆ˜ํ–‰ ํ›„ ๋‘๋ฒˆ์งธ ํŒ€ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. 1์ฐจ ํ”„๋กœ์ ํŠธ์—์„œ์˜ ๋ถ€์กฑํ–ˆ๋˜ ์ ๊ณผ ๋” ๋ฐฐ์šฐ๊ณ  ์‹ถ์—ˆ๋˜ ์ ์„ ๊นจ๋‹ฌ์•˜๊ณ , ์ด๋ฅผ ๋ณด์™„ํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•ด๋ณด์ž ํ•˜๋Š” ์ƒ๊ฐ์œผ๋กœ ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
์ด๋ฒˆ ํด๋ก  ์ฝ”๋”ฉ ํ”„๋กœ์ ํŠธ์˜ ์ฃผ์ œ๋Š” Nike ์›น์‚ฌ์ดํŠธ ์ž…๋‹ˆ๋‹ค.
๋‚˜์ดํ‚ค๋Š” ์Šคํฌ์ธ ์›จ์–ด ๋ธŒ๋žœ๋“œ ์ค‘์—์„œ ๊ฐ€์žฅ ์œ ๋ช…ํ•œ ๋ธŒ๋žœ๋“œ๋ผ๊ณ  ํ•ด๋„ ๊ณผ์–ธ์ด ์•„๋‹ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ‰์†Œ ๋‚˜์ดํ‚ค๋ฅผ ์ข‹์•„ํ•˜๋Š” ์ €์™€ ํฌ์œค๋‹˜์— ์˜ํ•ด Nike ๊ฐ€ ์ฑ„ํƒ๋˜์—ˆ๊ณ , 1์ฐจ ํ”„๋กœ์ ํŠธ์ธ ๋งˆํ”Œ์ƒต ํด๋ก ์ฝ”๋”ฉ๊ณผ ๊ฐ™์€ ์›น ์‡ผํ•‘๋ชฐ์ด๊ธฐ์— ์ง„ํ–‰ ๋ฐฉํ–ฅ์ด ๋น„์Šทํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฐจ์ด์ ์€ ๋ฆฌ์ŠคํŠธ ํŽ˜์ด์ง€ ๋‚ด์—์„œ์˜ ๋‹ค์–‘ํ•œ ํ•„ํ„ฐ๋ง SNKRS ํŽ˜์ด์ง€์—์„œ์˜ ์‘๋ชจ ๋ฐ ์ถ”์ฒจ ๊ธฐ๋Šฅ์ด์—ˆ์œผ๋ฉฐ, ๋ฐฑ์—”๋“œ๋ฅผ ๊ตฌํ˜„ํ•  ์ €์™€ ์ค€ํ˜๋‹˜์€ ๊ฐ์ž ์ด๋Ÿฌํ•œ ๋ฉ”์ธ ๊ธฐ๋Šฅ(ํ•„ํ„ฐ๋ง, SNKRS)์„ ํ•œ ๊ฐ€์ง€์”ฉ ๋ถ„๋‹ดํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค.


โญ๏ธ Team : ์—ญํ•  ๋ถ„๋‹ด

๋ฐฑ์—”๋“œ : ์ด์ค€ํ˜ ๊น€์˜์šฑ
ํ”„๋ก ํŠธ ์—”๋“œ : ํ™ฉํฌ์œค, ์ด์ง„์›…

์ ์šฉ ๊ธฐ์ˆ 

  • Front-End : React.js, Sass
  • Back-End : Node.js, Express, Prisma, nodemon, JWT, Bcrypt, My SQL, CORS
  • Common : RESTful API
  • Community Tools : Slack, Zoom, Notion

์ €๋ฒˆ ํ”„๋กœ์ ํŠธ๋Š” ํ”„๋ก ํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ๋ฅผ ๊ฐœ์ธ์ด ๋ชจ๋‘ ํ•œ๊ฐ€์ง€ ์ด์ƒ์„ ๊ตฌํ˜„ํ•˜๋ฉฐ Fullstack์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
1์ฐจ ํ”„๋กœ์ ํŠธ๋ฅผ ํ†ตํ•ด ํ”„๋ก ํŠธ ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ ์ค‘ ์–ด๋–ค ๊ฒƒ์ด ๊ฐ์ž ์ž์‹ ์—๊ฒŒ ์ ํ•ฉํ•œ์ง€๋ฅผ ํŒŒ์•… ๊ฐ€๋Šฅํ–ˆ์œผ๋ฉฐ, ์ด๋ฒˆ 2์ฐจ ํ”„๋กœ์ ํŠธ๋Š” ํ”„๋ก ํŠธ ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ์˜ ์—ญํ• ์„ ๋ช…ํ™•ํžˆ ๋‚˜๋ˆ„์–ด, ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๋ฐฑ์—”๋“œ ์™ธ์˜ ์—ญํ• ์ด ์žˆ์—ˆ๋‹ค๋ฉด, ํ”„๋ก ํŠธ ์—”๋“œ๋‹จ์—์„œ ์‚ฌ์šฉํ•  ์ด๋ฏธ์ง€(์ œํ’ˆ ์ด๋ฏธ์ง€)๋Š” ์ˆœ์กฐ๋กœ์šด ์ž‘์—…์„ ์œ„ํ•ด ๋ฐฑ์—”๋“œ์ธ ์ €์™€ ์ค€ํ˜๋‹˜์ด ์ž๋ฃŒ ์ˆ˜์ง‘์„ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
๊ทธ ์™ธ์—” ํ”„๋ก ํŠธ ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ์˜ ์ž‘์—…๋“ค์ด ๋ชจ๋‘ ์—ญํ•  ๋ถ„๋‹ด์„ ํ†ตํ•ด ์ด๋ฃจ์–ด์กŒ์Šต๋‹ˆ๋‹ค.


๐Ÿณ Nike Web Page

๋‚˜์ดํ‚ค ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€์ž…๋‹ˆ๋‹ค. ํ•ด๋‹น ํŽ˜์ด์ง€์˜ ํฐ ๋ฒ”์ฃผ์ธ ๋ฉ”์ธ ํŽ˜์ด์ง€, ๋ฆฌ์ŠคํŠธ ํŽ˜์ด์ง€ ๋ฐ ํ•„ํ„ฐ ๊ธฐ๋Šฅ, ๋””ํ…Œ์ผ ํŽ˜์ด์ง€ ์ž…๋‹ˆ๋‹ค.

(๋‚˜์ดํ‚ค ๋””ํ…Œ์ผ ํŽ˜์ด์ง€ / ์ถœ์ฒ˜ : ๋‚˜์ดํ‚ค ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€)


(๋‚˜์ดํ‚ค ๋ฆฌ์ŠคํŠธ ํŽ˜์ด์ง€ / ์ถœ์ฒ˜ : ๋‚˜์ดํ‚ค ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€)


(๋‚˜์ดํ‚ค ๋ฆฌ์ŠคํŠธ ํŽ˜์ด์ง€ - ํ•„ํ„ฐ๋ง (1) / ์ถœ์ฒ˜ : ๋‚˜์ดํ‚ค ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€)


(๋‚˜์ดํ‚ค ๋ฆฌ์ŠคํŠธ ํŽ˜์ด์ง€ - ํ•„ํ„ฐ๋ง (2) / ์ถœ์ฒ˜ : ๋‚˜์ดํ‚ค ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€)


(๋‚˜์ดํ‚ค ๋””ํ…Œ์ผ ํŽ˜์ด์ง€ / ์ถœ์ฒ˜ : ๋‚˜์ดํ‚ค ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€)


๐ŸŒˆ ์นด์นด์˜ค ๋กœ๊ทธ์ธ ์—ฐ๋™ ๊ตฌํ˜„

1์ฐจ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋กœ๊ทธ์ธ๊ณผ ํšŒ์›๊ฐ€์ž… API๋ฅผ ์ž์ฒด์ ์œผ๋กœ ๊ฐœ๋ฐœํ•˜์—ฌ ๋กœ๊ทธ์ธ ํšŒ์›๊ฐ€์ž… ๊ธฐ๋Šฅ๊ณผ ๊ทธ์— ๋”ฐ๋ฅธ ์ธ๊ฐ€ ๋ถ€์—ฌ๋ฅผ ๊ตฌํ˜„ํ•˜์˜€๋‹ค๋ฉด, ์ด๋ฒˆ์—๋Š” ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ์—ฐ๋™์‹œ์ผœ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด์ฃผ๋„๋ก ์ง„ํ–‰ํ•˜์˜€๋‹ค. ์‚ฌ์‹ค ์ด ๋ถ€๋ถ„์€ ๊ตฌ๊ธ€๋ง๊ณผ ์œ ํŠœ๋ธŒ, Kakao Developers ์‚ฌ์ดํŠธ์˜ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ํ†ตํ•ด ๊ณต๋ถ€๋ฅผ ํ•ด๋ณธ ๊ฒฐ๊ณผ ๋งŽ์€ ์ž๋ฃŒ๋“ค๊ณผ ๋ฐฉ๋ฒ•๋“ค์ด ์žˆ์—ˆ๊ณ , ์ฝ”๋“œ ์ž์ฒด๊ฐ€ ์–ด๋ ค์šด ๊ฑด ์•„๋‹ˆ์—ˆ์ง€๋งŒ, ์ด๋ฅผ ์ ์šฉ์‹œํ‚ค๋Š” ๊ณผ์ •๊ณผ ๊ทธ๊ฒƒ๋“ค์„ ์ดํ•ดํ•˜๋Š”๋ฐ ๋งŽ์€ ์‹œ๊ฐ„์„ ๋ณด๋ƒˆ๋‹ค.

์นด์นด์˜ค ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ์—ฐ๋™ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์–‘ํ•˜๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๊ณต์‹ ๋ฌธ์„œ์™€ ๋งŽ์€ ๋ธ”๋กœ๊ทธ๋ฅผ ์ฐธ๊ณ ํ•˜์˜€์œผ๋ฉฐ, ํ•„์ž๋Š” REST API KEY๋ฅผ ํ™œ์šฉํ•˜์—ฌ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ Access Token๊ณผ Refresh Token์„ ๋ฐœ๊ธ‰ ๋ฐ›์•„ ์œ ์ € ์ •๋ณด๋ฅผ ๋ฐ›์•„์˜ค๋„๋ก ์ฒ˜๋ฆฌํ•ด์ฃผ์—ˆ๋‹ค. ๋ฐ›์•„์˜จ ์œ ์ € ์ •๋ณด๋ฅผ ๋ฐฑ์—”๋“œ์— ๋„˜๊ฒจ์ฃผ์–ด, ํ•ด๋‹น ์œ ์ € ์ •๋ณด์˜ DB ๋‚ด์˜ ์œ ๋ฌด๋ฅผ ํ†ตํ•ด ์ดํ›„์˜ ์ธ์ฆ ์ธ๊ฐ€ ๊ณผ์ •์„ ์ฒ˜๋ฆฌํ•ด์ฃผ๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์˜€๋‹ค. (ํ—ˆ๋‚˜, ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์€ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ๋Š” Access Token์„ ๋ฐ›์•„ ์ด๋ฅผ Token์œผ๋กœ ๋ฐฑ์—”๋“œ๋กœ ๋„˜๊ฒจ์ฃผ๋Š” ๊ฒƒ์—์„œ ๋ฉˆ์ถ”๊ณ , ๋ฐฑ์—”๋“œ์—์„œ ๋‚˜๋จธ์ง€ ๊ณผ์ •์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•˜๋‹ค๊ณ  ํ•˜๋‹ค. ์ด๋Ÿฌํ•œ ์ž‘์—… ๋˜ํ•œ ์ถ”ํ›„ ๋‹ค์‹œ ์ง„ํ–‰ํ•˜์˜€์œผ๋ฉฐ ์ดํ›„ ํฌ์ŠคํŒ…์—์„œ ๋‹ค๋ฃฐ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.)


โšก๏ธ ํ”„๋ก ํŠธ์—”๋“œ ๊ตฌํ˜„ (Code > Access Token > ์œ ์ € ์ •๋ณด ๋ฐ˜ํ™˜)

Auth.js

import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import qs from 'qs';
import { CLIENT_SECRET, REDIRECT_URI, REST_API_KEY } from '../../config';

const Auth = () => {
  // calllback์œผ๋กœ ๋ฐ›์€ ์ธ๊ฐ€์ฝ”๋“œ

  // ๋กœ๊ทธ์ธ ํ›„์˜ URL ๋‚ด์˜ code get
  const code = new URL(window.location.href).searchParams.get('code');

  const navigate = useNavigate();

  // ๊ณ ์œ  REST API KEY , REDIRECT URI, CLIENT SECRET KEY, Code ๋ฅผ ํ†ตํ•ด Access Token ๋ฐ˜ํ™˜
  const getToken = async () => {
    const payload = qs.stringify({
      grant_type: 'authorization_code',
      client_id: REST_API_KEY,
      redirect_uri: REDIRECT_URI,
      code: code,
      client_secret: CLIENT_SECRET,
    });
    try {
      // access token ๊ฐ€์ ธ์˜ค๊ธฐ
      const res = await axios.post('https://kauth.kakao.com/oauth/token', payload);

      // Kakao Javascript SDK ์ดˆ๊ธฐํ™”
      window.Kakao.init(REST_API_KEY);

      // access token ์„ค์ •
      window.Kakao.Auth.setAccessToken(res.data.access_token);

      navigate('/login');
    } catch (err) {
      console.log(err);
    }
  };

  useEffect(() => {
    getToken();
  }, []);

  return null;
};

// Kakao SDK API๋ฅผ ์ด์šฉํ•ด ์‚ฌ์šฉ์ž ์ •๋ณด ํš๋“

export default Auth;

์•ก์„ธ์Šค ํ† ํฐ์„ ๋ฐ›์•„์˜จ ํ›„ useNavigate() ํ›…์„ ํ†ตํ•ด /login ํŽ˜์ด์ง€์—์„œ ์œ ์ € ์ •๋ณด๋ฅผ ๋ฐ›์•„์˜ด (ํ•˜๋‹จ ์ฝ”๋“œ ์ฐธ์กฐ)

Login.js

import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import { GET_SIGN_IN_API } from '../../config';

function Login() {
  const [email, setEmail] = useState();
  const [name, setName] = useState();
  const [token, setToken] = useState();
  const navigate = useNavigate();

  // v2/user/me ์— ํ•ด๋‹น ์œ ์ €์˜ ์ •๋ณด(์ด๋ฉ”์ผ, ๋‹‰๋„ค์ž„, ํ”„๋กœํ•„ ์‚ฌ์ง„ ๋“ฑ์˜ ์ •๋ณด)๋ฅผ ๋ฐ˜ํ™˜
  const getLocal = async () => { 
    // Kakao SDK API๋ฅผ ์ด์šฉํ•ด ์‚ฌ์šฉ์ž ์ •๋ณด ํš๋“
    let data = await window.Kakao.API.request({
      url: '/v2/user/me',
    });

    // ์œ ์ € ์ •๋ณด(์ด๋ฉ”์ผ, ๋‹‰๋„ค์•”) state์— ์ €์žฅ
    setEmail(data.kakao_account.email);
    setName(data.properties.nickname);
    alert(`${data.properties.nickname}๋‹˜ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค!`);
  };
}

์œ„ ๋ฐฉ์‹์€ ํ”„๋ก ํŠธ์—”๋“œ ๋‚ด์—์„œ ์ž์ฒด์ ์œผ๋กœ code > Access Token > ์œ ์ € ์ •๋ณด๋ฅผ ์ž์ฒด์ ์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ณผ์ •์ด๋‹ค. ์„ฑ๊ณต์ ์œผ๋กœ ์œ ์ € ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ํ™•์žฅ์„ฑ์„ ๊ณ ๋ คํ–ˆ์„ ๋•Œ ์ ํ•ฉํ•˜์ง€ ์•Š๋‹ค. ์ด์œ ๋Š” ๋ฐฑ์—”๋“œ์—์„œ Access Token์„ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๋Š” ์ž‘์—…์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ธ๊ฐ€ ๋ถ€์—ฌ ๊ณผ์ •์„ ์ ํ•ฉํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•˜๊ณ , ํ™•์žฅ์„ฑ์„ ๊ณ ๋ คํ•˜์ง€ ๋ชปํ•œ ๋ถ€๋ถ„์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ ‡๊ธฐ์—, ํ”„๋ก ํŠธ์—”๋“œ์—์„œ๋Š” Access Token๋ฐ˜ํ™˜ ๊ณผ์ •๊นŒ์ง€๋งŒ ๊ฑฐ์น˜๊ณ , Access Token์„ ๋ฐฑ์—”๋“œ์— ๋„˜๊ฒจ์ฃผ๊ณ , ๋ฐฑ์—”๋“œ์—์„œ Access Token์„ ํ†ตํ•ด, ๋กœ๊ทธ์ธ, ์œ ์ € ๋“ฑ๋ก, ๋ฉค๋ฒ„์‰ฝ ์ธ๊ฐ€ ๊ธฐ๋Šฅ์„ ์ฒ˜๋ฆฌํ•˜์ฃผ๋„๋ก ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์˜€๋‹ค.


โšก๏ธ ๋ฐฑ์—”๋“œ ๊ตฌํ˜„ (Access Token > ์œ ์ € ์ •๋ณด ๋ฐ˜ํ™˜ > ๋กœ๊ทธ์ธ, ์œ ์ € ๋“ฑ๋ก, ๋ฉค๋ฒ„์‰ฝ ์ธ๊ฐ€ API ๊ตฌํ˜„)

NewAuth.js (ํ”„๋ก ํŠธ ์—”๋“œ์—์„œ Access Token์„ ๋ณด๋‚ด์ฃผ๋Š” ์ž‘์—…)

import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import qs from 'qs';
import { CLIENT_SECRET, REDIRECT_URI, REST_API_KEY, GET_SIGN_IN_API } from '../../config';

const Auth = () => {
  const navigate = useNavigate();
  const [token, setToken] = useState();

  // calllback์œผ๋กœ ๋ฐ›์€ ์ธ๊ฐ€์ฝ”๋“œ
  const code = new URL(window.location.href).searchParams.get('code');

  useEffect(() => {
    try {
      axios
        .post(
          'https://kauth.kakao.com/oauth/token',
          qs.stringify({
            grant_type: 'authorization_code',
            client_id: REST_API_KEY,
            redirect_uri: REDIRECT_URI,
            code: code,
            client_secret: CLIENT_SECRET,
          })
        )
        .then(res =>
          axios
            .get(GET_SIGN_IN_API, { headers: { accessToken: res.data.access_token } })
            .then(res => {
              setToken(res.data.token);
              localStorage.setItem('token', token);
            })
        )
        .then(alert(`ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค!`))
        .then(navigate('/'));
    } catch (err) {
      alert('๋กœ๊ทธ์ธ ์‹คํŒจ');
    }
  }, []);

  return null;
};

export default Auth;

์ด์ „ ์ฝ”๋“œ์™€ ๋‹ค๋ฅธ ์ ์€ Access Token ์„ SignIn API์— ํ—ค๋”๋ฅผ ํ†ตํ•ด ๋ณด๋‚ด์ฃผ์–ด get์„ ํ†ตํ•ด ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. ๊ทธ ํ›„ SignIn API๋ฅผ ํ†ตํ•ด ๋ฐ›์•„์˜จ ์ธ๊ฐ€ Token์„ ํ†ตํ•ด ์ธ์ฆ๊ณผ ์ธ๊ฐ€ ์ฒ˜๋ฆฌ๋ฅผ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๊ตฌํ˜„ํ•œ๋‹ค.


userController.js

import { userServices } from '../services';

const signIn = async (req, res) => {
  try {
    const accessToken = req.headers.accesstoken;
    if (!accessToken) {
      return res.status(401).json({ message: 'INVALID_KAKAO_TOKEN' });
    }
    const token = await userServices.signIn(accessToken);

    return res.status(201).json({ message: 'LOGIN_SUCCESS', token });
  } catch (err) {
    console.log(err);
    return res.status(500).json({ message: err.message });
  }
};

userServices.js

import { userDao } from '../models';
import token from '../utils/token';
import axios from 'axios';

const signIn = async accessToken => {
  // accessToken์œผ๋กœ kakao API์— ์ ‘๊ทผํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ •๋ณด ์ทจ๋“
  const user = await axios.get('https://kapi.kakao.com/v2/user/me', {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'Content-type': 'application/x-www-form-urlencoded;charset=utf-8',
    },
  });

  // ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ์—๋Ÿฌ ์ฒ˜๋ฆฌ
  if (!user.data) {
    const err = new Error('INVALID_USER');
    err.statusCode = 400;

    throw err;
  }

  const data = user.data;
  
  // ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ๋‚ด์— ํ•ด๋‹น ์ด๋ฉ”์ผ์˜ ์œ ์ € ์ •๋ณด๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ
  const isExist = await userDao.isExistEmail(data.kakao_account.email);
  let userId;

  // ํ•ด๋‹น ์œ ์ € ์ •๋ณด๊ฐ€ ์ด๋ฏธ ์žˆ์„ ๊ฒฝ์šฐ, ํ•ด๋‹น userId์˜ ํ† ํฐ์„ ๋ฐ˜ํ™˜
  if (isExist) {
    userId = await userDao.getUserId(data.kakao_account.email);
    return token.signToken(userId);
  }

  // ํ•ด๋‹น ์œ ์ € ์ •๋ณด๊ฐ€ ์—†์„ ๊ฒฝ์šฐ, ํšŒ์› ์ •๋ณด ์ƒ์„ฑ
  await userDao.createUser(data.kakao_account.email, data.properties.nickname);
  // ์ƒ์„ฑ๋œ ์œ ์ €์˜ userId์™€ ํ† ํฐ ๋ฐ˜ํ™˜
  userId = await userDao.getUserId(data.kakao_account.email);
  const signToken = token.signToken(userId);
  return signToken;
};

userDao.js

import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

// email์„ ํ†ตํ•ด userId ๋ฐ˜ํ™˜
const getUserId = async email => {
  const createData = await prisma.$queryRaw`
        SELECT
          id 
        FROM 
          users
        WHERE
          email=${email};
        `;

  return createData;
};


// email, name ์„ ํ†ตํ•œ ํšŒ์› ๊ฐ€์ž… (์œ ์ € ๋“ฑ๋ก)
const createUser = async (email, name) => {
  const createData = await prisma.$queryRaw`
        INSERT INTO 
          users (email, name) 
        VALUES 
          (${email}, ${name});
        `;

  return createData;
};

// ํ•ด๋‹น email์˜ ์œ ์ € ์ •๋ณด์˜ ์ค‘๋ณต ์—ฌ๋ถ€ ํŒŒ์•…
const isExistEmail = async email => {
  const [user] = await prisma.$queryRaw`
    SELECT 
      email,name 
    FROM 
      users 
    WHERE 
      email = ${email};
    `;
  return user;
};

ํ”„๋ก ํŠธ ์—”๋“œ์—์„œ์˜ ์นด์นด์˜ค ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์˜ ๋ฌธ์ œ์ ์„ ํŒŒ์•…ํ•˜๊ณ , ๋ฐฑ์—”๋“œ์—์„œ Access Token์„ ํ™œ์šฉํ•ด ๋กœ๊ทธ์ธ ๋ฐ ์œ ์ € ๋“ฑ๋ก์‹œ์— ํ•„์š”ํ•œ ์œ ์ € ์ •๋ณด๋ฅผ axios์™€ kakao developers ์˜ ์ž๋ฃŒ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•˜๋Š” API๋ฅผ ๊ตฌํ˜„ํ•˜์˜€๊ณ , ๋ฐ˜ํ™˜ํ•œ ์œ ์ € ์ •๋ณด๋ฅผ ํ†ตํ•ด User์™€ ๊ด€๋ จ๋œ ๋ชจ๋“  API (๋กœ๊ทธ์ธ, ํšŒ์›๊ฐ€์ž…, ๋ฉค๋ฒ„์‰ฝ ์ธ๊ฐ€ ๊ตฌํ˜„, ๋ฆฌ๋ทฐ ๋“ฑ๋ก, ๋ฆฌ๋ทฐ ํ‰๊ท  ๋ฐ˜ํ™˜)๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ดˆ๊ธฐ๋ถ€ํ„ฐ ํ™•์žฅ์„ฑ๊ณผ ๋ณ€๊ฒฝ ์šฉ์ด์„ฑ์„ ์šฐ์„ ์ ์œผ๋กœ ๊ณ ๋ คํ•ด์•ผ๋œ๋‹ค๋Š” ํ”ผ๋“œ๋ฐฑ์ด ๋˜์—ˆ๋˜ ์ž‘์—…์ด์—ˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ  ๋งํฌ


profile
๋„˜์–ด์ ธ๋„ ์ผ์–ด๋‚˜์„œ ๋‚˜์•„๊ฐ€๋Š” ๊ฐœ๋ฐœ์ž์˜ ์‚ถ์„ ์—ผ์›ํ•ฉ๋‹ˆ๋‹ค.

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