간편 로그인 구현 방법 : OAuth

코딩로그·2025년 3월 20일
0
post-thumbnail

OAUTH (Open Authorization)

  • 이미 사용자 정보를 갖고 있는 서비스가 다른 서비스에서의 사용자 인증을 대신 해주는 방법
  • 대표적인 사례 : 간편 로그인
    • 내 정보를 직접 입력하는 과정이 필요 없음
    • 이미 가입되어있는 서비스에서 인증 후 내 정보를 대신 보내줌

OAuth의 주체

  • 사용자 (Resource Owner)
  • 새로운 서비스 (Application : Client & Server)
  • 사용중인 서비스 (Resource Server & Authorization Srver)

OAuth의 흐름

  • 인증을 요청한 어플리케이션 측에서 엑세스 토큰을 서버에서 클라이언트에 넘겨주고 클라이언트에서 관리하는 경우도 있음

OAuth 구현하기 실습

인터페이스 생성

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="style.css" />
</head>
<body>
  <h1>OAuth 실습</h1>
  <button id="kakao">카카오 로그인</button>
  <button id="naver">네이버 로그인</button>
  <main>
    <img />
    <div>유저 이름 : <span id="user_name"></span></div>
    <button id="logout_button">로그아웃</button>
  </main>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script src="login.js"></script>
</body>
</html>

style.css

body {
  padding: 0 20px;
}

main {
  border: 1px solid gray;
  border-radius: 10px;
  padding: 20px;
  margin-top: 20px;
  width: 200px;
  display: grid;
  place-items: center;
}

div {
  margin-bottom: 20px;
}

button {
  padding: 10px 20px;
  margin-right: 6px;
}

#logout_button {
  width: 100%;
  height: 50px;
}

img {
  width: 100px;
  height: 100px;
  background-color: gray;
  border-radius: 100%;
  margin-bottom: 10px;
  object-fit: cover;
}

서버 생성 및 환경 설정

npm 패키지 설치

npm init -y
npm install express cors axios
  • 설치 후 package.json에서 패키지 확인
{
  "name": "oauth",
  "version": "1.0.0",
  "main": "login.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "axios": "^1.7.4",
    "cors": "^2.8.5",
    "express": "^4.19.2"
  }
}

server.js

  • 설치한 패키지 변수에 할당
const express = require('express');
const cors = require('cors');
const axios = require('axios');
  • 서버 생성
const app = express();
  • cors 설정
app.use(cors({
    origin: ["http://127.0.0.1:5500", "http://localhost.1:5500"],
    //로그인할 때 사용할 POST와 로그아웃할 때 사용할 DELETE
    methods: ["OPTIONS", "POST", "DELETE"],
    // 데이터를 쿠키로 주고받을 경우에는 credentials 설정 필요
}))
  • json 파싱 메소드 설정
app.use(express.json());
  • 코드 실행 메서드
    • app.listen(port, callback)
      • Express 서버를 특정 포트에서 실행하는 메서드
      • port: 서버가 실행될 포트 번호
      • callback: 서버가 정상적으로 실행되었을 때 실행되는 콜백 함수
app.listen(3000, () => console.log('3000번 포트에서 서버 열림'))

클라이언트 설정

login.js에서 요소 연결

const kakaoLoginButton = document.querySelector("#kakao");
const naverLoginButton = document.querySelector("#naver");
const userImage = document.querySelector("img");
const userName = document.querySelector("#user_name");
const logoutButton = document.querySelector("#logout_button");

카카오 로그인 구현

인증 요청을 보내기 위한 환경 설정

내 애플리케이션에서 소셜 로그인을 이용하고자 하는 어플 등록

어플리케이션에 들어가 설정에서 카카오 로그인 상태를 ON으로 변경

Redirect URI 설정

  • Redirect URI
    • Authorization 코드를 받을 클라이언트 주소


동의항목 설정

  • 필요한 항목요소의 설정에서 동의 단계 및 동의 목적을 선택한 후 저장

앱 키 받으러 가기

  • REST API키를 사용
    • 소셜로그인 기능을 가져다 쓸 때 개발 어플은 서비스 이용자
    • 서비스 이용자로서 로그인 하는 아이디 겸 키 역할

  • login.js에 해당 키를 변수로 할당
const kakaoClientId = 'd14c29937265b0e48b1765ee823da865'
  • 카카오 로그인 > 보안 탭에 접속, 비밀번호 역할을 하는 Client Secret키도 발급할 수 있음
    • 노출되면 안되므로 .env 등 src 파일 외부 환경변수 파일에 저장 후 gitignore로 관리해야함

카카오 로그인 인가코드 받기

카카오 로그인 기능을 구현하는 클라이언트 측(JavaScript) 코드

  • 사용자가 로그인 버튼을 클릭하면 카카오 로그인 페이지로 이동
  • 로그인 성공 시 인가 코드 포함된 URL로 리디렉트됨
const kakaoClientId = [발급받은 Client ID]
const redirectURI = 'http://127.0.0.1:5500'

kakaoLoginButton.onclick = () => {
    // 화면을 호출하기 위한 이동 코드
    location.href = `https://kauth.kakao.com/oauth/authorize?client_id=${kakaoClientId}&redirect_uri=${redirectURI}&response_type=code`
}

실행 결과

로그인 후, 카카오에서 인가 코드 받기

window.onload = () => {
    // 현재 페이지의 URL을 가져옴
    const url = new URL(location.href);
    const urlParams = url.searchParams;

    // 카카오가 리디렉트하면서 URL에 추가한 'code' 값을 가져옴
    const authorizationCode = urlParams.get('code');
    console.log(authorizationCode);
};

서버에서 로그인 요청을 위한 authorization code 전달

app.post('/kakao/login', (req, res) => {
    const authorizationCode = req.body.authorizationCode;
    axios.post('https://kauth.kakao.com/oauth/token', {
        grant_type : 'authorization_code',
        client_id : kakaoClientId,
        redirect_uri : redirectURI,
        code : authorizationCode
    }, {
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
        }
    }).then(response => res.send(response.data.access_token))
})

로그인 성공 후 유저 정보 받아오기

유저 정보를 가져오기 위한 클라이언트 코드 작성

    axios.post('http://localhost:3000/kakao/login', {
        authorizationCode})
        .then(res => {
            kakaoAccessToken = res.data;
            return axios.post('http://localhost:3000/kakao/userinfo', {kakaoAccessToken})
        })
        .then(res => {
            console.log(res.data)
            const userInfo = res.data
            renderUserInfo(userInfo.profile_image, userInfo.nickname)
        });
}

kakao에 유저 정보를 가져오기 위한 get 요청 처리

app.post('/kakao/userinfo', (req, res) => {
    const {kakaoAccessToken} = req.body;
    axios.get('https://kapi.kakao.com/v2/user/me', {
        headers : {
            Authorization : `Bearer ${kakaoAccessToken}`,
            'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
        }
    }).then(response => {
        console.log(response.data.properties)
        res.send(response.data.properties)
    })
})

로그아웃을 위한 클라이언트측 코드 작성

logoutButton.onclick = () => {
    axios.delete('http://localhost:3000/kakao/logout', {
        data : {kakaoAccessToken}
    }).then(res => {
        console.log(res.data)
        renderUserInfo('', '')
    })
}

kakao에 로그아웃 요청을 하기 위한 delete 요청 처리

app.delete('/kakao/logout', (req, res) => {
    const {kakaoAccessToken} = req.body
    console.log(kakaoAccessToken)
    axios.post('https://kapi.kakao.com/v1/user/logout', {} ,{
        headers : {
            Authorization : `Bearer ${kakaoAccessToken}`,
    }}).then(response => res.send('로그아웃 성공!'))
})
profile
hello world!

0개의 댓글