본문 바로가기

공부기록용

[ React ] 카카오 api로 사용자 정보 받아오기

***개인적인 공부 기록용이라 두서없고, 정신없이 기록

오늘의 수확: 인가코드 발급받기, 액세스토큰 발급받기, 액세스토큰으로 사용자 정보 접근하여 받아오기

 

Kakao 소셜 로그인으로 사용자 정보 가져오기

 

먼저 React 프로젝트 만들기

 

1. 새로운 리액트 프로젝트를 만들 폴더 안에서 터미널 열고, npx create-react-app 프로젝트명 입력하기

npx create-react-app myapp 입력한 후 엔터치면 패키지들이 설치된다.

create react app(cra)을 사용하여 react 프로젝트를 만들 때, 기본적으로 포함되는

react, react-dom, react-scripts 패키지와 cra-template 템플릿을 함께 사용하는 것을 의미한다.

--> cra-template은 create react app(cra)을 사용할 때, 기본적으로 제공되는 템플릿이다.

이 템플릿을 통해 react 애플리케이션을 빠르게 시작할 수 있도록 초기 설정 파일과 디렉토리 구조를 제공한다.

 

마지막에 Happy hacking! 이라는 텍스트가 보이면 리액트 프로젝트가 생성됐다는 것이다.

 

2. 생성된 프로젝트명(폴더) 안에서 터미널 열고 npm start 입력하기

생성된 프로젝트 안으로 경로를 이동한 뒤: cd myapp

만들어진 프로젝트를 웹 브라우저에서 동작하게 한다: npm start

 

문제가 없다면 Local 주소가 출력되면서 브라우저가 켜지고, 리액트 기본 화면?이 보이게 된다.


생성된 리액트 프로젝트 구조 살펴보기

 

create-react-app 라이브러리를 사용하기 위해서 Node.js가 필요하기 때문에 Node.js를 설치

Node.js를 설치하면 npm이라는 도구도 함께 다운로드 된다. npm은 node.js를 설치한 컴퓨터에서

라이브러리를 쉽게 설치할 수 있도록 도와주는 도구다. npx[npm] create-react-app 새로운프로젝트명

 

프로젝트가 생성되고,

메인 페이지에 들어갈 HTML을 짜는 곳이 App.js가 된다. app.js의 위치는

 

프로젝트명

└ node_modules --- 라이브러리 모은 폴더

└ public --------------- static 파일(이미지, 동적으로 바뀌지 않는 파일들) 보관함

      └ index.html ----- 메인페이지

└ src ------------------- 소스코드 보관함

      └ App.js------------ 메인페이지에 들어갈 HTML을 짜는 곳

package.json --------- 설치한 라이브러리 목록

 

메인 페이지(index.html)에 구성될 내용을 App.js에서 작성하고, App.js는 index.js를 통해서 index.html에 렌더링?된다.


FSD 아키텍처(폴더구조가 복잡할 때)

폴더구조는 최대한 3단계: 먼저 레이어(Layers) 폴더 안에 Slices 폴더가 존재하고, Slices 폴더 안에 Segments 폴더가 존재한다.

Layers: import 관계의 위계질서 확립 Slices Segment
app: 프론트앱 또는 모바일앱의 전체적인 설정을 다루는 폴더(전역 컴포넌트) user  
processes: 페이지들이 여러 개 연달아 잇는 것  
pages: 브라우저 주소 단위의 컴포넌트 post  
widgets: layout  
features: 컴포넌트 폴더, 데이터를 제외한 기능 comment  
entities: 컴포넌트 폴더, 데이터  
shared: hook, util 등 공유하는 파일 보관 폴더 shared는 slices는 없다.  

 


ESLint와 prettier 설정하기

 

1. eslint 설치하기: npm install eslint --save-dev

라이브러리를 설치하기 위한 npm install <module-name> --save

--save 옵션은 package.json의 dependency 항목에 모듈을 추가한다는 의미다.

 

package.json의 dependencies 항목을 보고 npm이 자동으로 프로젝트에 의존된 모듈을 다운로드해준다.

package.json에 추가되지 않는다는 것은 해당 모듈을 기억하고, 관리하지 않겠다는 의미와 같음

만약 node_modules를 제거하게 되면 --save하지 않은 라이브러리는 다시 추가하지 않는다.

하지만 npm5부터는 --save 옵션을 기본 옵션으로 적용하게 되어 --save를 사용하지 않아도 dependencies 항목에 추가된다.

 

내가 지금 사용하는 npm 버전이 10이니 --save 옵션이 기본 옵션으로 적용되어 --save를 생략해도 된다.

--save-prod or -P: package.json의 dependencies에 패키지를 등록한다(default) --- 기본값

--save 외에 -dev 옵션 또한 eslint를 설치할 때 작성했는데,

-dev는 package.josn의 devDependencies에 패키지(모듈)를 등록한다는 의미다.

 

--save-dev 또는 -D의 옵션을 작성한다.

***package.json의 devDependencies와 dependencies의 차이

dependencies는 프로젝트 작업 중에 npm install 명령어를 통해 설치된 라이브러리들이 작성된다.

애플리케이션을 배포할 때 사용할 라이브러리들이 담겨있는 곳이다. 애플리케이션의 동작을 도와주는 라이브러리들이 저장된다.

dependencies에 설치된 라이브러리는 배포할 때 포함된다.

 

devDependencies는 애플리케이션이 작동하는데

직접적인 연관은 없지만 개발할 때 필요한 라이브러리들을 설치한다.

어플의 로직과는 직접적인 연관이 없는 라이브러리들이 모여있는 곳이다.

 

```

#devDependencies에 라이브러리를(모듈) 포함하는 명령어

 

npm install eslint --save-dev

 

or

 

npm i eslint -D

 

```

 

npm install eslint -D로 eslint를 설치했으면 eslint 설정을 초기화해준다.

npx eslint --init

위 명령어를 입력하면 eslint 초기화 설정에 대한 여러 질문들을 통해 설정된다.

 

설치하고, 초기화를 나누어서 할 수 있지만 한 번에 하는 방법도 있다.

npm install @eslint/config@latest    ---> 이 명령어를 사용해서 ESLint를 설치하고 설정할 수 있다.

 

a specific sharable config that is hosted on npm → npm에서 제공되고 있는[호스팅된] 특정 공유가능한 설정

 

If you want to use a specific sharable config that is hosted on npm,

you can use the --config option and specify the package name

npm에서 제공되고 있는, npm을 통해서 다운로드 받을 수 있는,

특정 공유가능한 설정을 사용하고 싶다면, --config 옵션을 사용하여 패키지명을 지정할 수 있다.

 

공유가능한 설정이란 것은 다른 사람들이 미리 정의해놓고 공유하는 설정을 말한다.

예를 들어 코드 스타일을 정하는 ESLint 설정이나 빌드 도구에 대한 설정 등이다.

처음부터 끝까지(a부터 z까지) eslint의 설정을 내가 하는 것이 아니라

이미 코드의 문법을 검사하거나 문제를 찾는 eslint 설정을 잘 해 놓은 것이 있다면

npm을 통해서 --config 옵션과 특정 package이름을 작성함으로써 그 설정을 다운받을 수 있다.

 

Note: npm init @eslint/config@latest 명령어를 사용한다는 것은 사용하는 프로젝트에서

package.json 파일이 이미 존재한다고 추정한다. 만약 package.json 파일이 존재하지 않는다면

eslint를 설치하고, 설정하기 전에 반드시 npm init을 해야한다.

 

eslint를 초기화 설정한 뒤에 추가적으로 리액트 규칙이 포함되어 있는? 설정을 추가적으로 다운받으려고 했다.

npm install -D eslint-config-airbnb --> 리액트 관련 규칙들이 포함되어 있어 다운로드 받으면

eslint-plugin-import, eslint-plugin-react, eslint-plugin-react-hooks, eslint-plugin-jsx-a11y 항목들이

devDependencies에 포함된다고 했는데 에러가 났다.

 

```문제 해결 과정

1. npm error Found: eslint@9.15.0 ---> eslint의 버전이 9.15.0버전이라서 문제가 생긴 것 같은데

루트 프로젝트(the root project)의 dev eslint 버전이 9.15.0버전이라고 출력되어있다.

peer d

 

devDependencies의 eslint 버전을 보니까 ^8.57.1 8버전인데?

 

2. npm error peer eslint@"^7.32.0 || ^8.2.0" from eslint-config-airbnb@19.0.4

eslint-config-airbnb 패키지는 eslint 버전이 ^7.32.0 또는 ^8.2.0 이어야 한다는 의미란다.

eslint 버전이 9.x.x이면 호환이 되지 않는다는데,

 

eslint-config-airbnb는 eslint 패키지에 의존하는데, 특정 버전 범위를 요구한다.

^7.32.0은 7.32.0 이상 8.0.0 미만 버전을 의미하고,

^8.2.0은 8.2.0 이상 9.0.0 미만 버전을 의미한다.

eslint-config-airbnb는 eslint 버전이 7.32.0 이상 8.0.0 미만 또는 8.2.0 이상 9.0.0 미만이어야 한다고 요구하는 것

 

eslint버전을 낮추거나

eslint버전과 호환이 가능한 eslint-config-airbnb버전을 설치하는 것

 

package.json에 작성되어 있는 devDependencies에 작성된

eslint와 명령어로 확인한 eslint의 버전이 달라 일치시키기 위해

node_modules와 package.lock.json을 삭제하고

npm i 명령을 실행함으로써 라이브러리를 재다운받음 --> 버전 여전히 불일치

그래서 npm install eslint@8.2.0 -D 명령을 함으로써 다시 eslint의 버전을 낮춰보겠다.

그리고 npm cache clean --force 로 캐시를 삭제해 주었다.

그리고 npm install -D eslint-config-airbnb 를 명령했더니 설치가 되었다.

 

그리고 airbnb에서 제작한 리액트 관련 규칙을 설정해주는 플러그인을 설치함

npm i -D eslint-plugin-prettier eslint-config-prettier

 

**네트워크 연결 문제

npm error cod EHOSTUNREACH

npm error errno EHOSTUNREACH

 

**npm cache 삭제

npm cache clean --force

 

eslint 9.x버전에서 .eslint.rc 파일 eslint.config.js 파일로 바뀜

 

eslint-config-airbnb: 에어비엔비 린트 플러그인, eslint 버전 확인 요망

eslint-config-next: Next.js 전용 린트 플러그인

eslint-plugin-react: React 전용 린트 플러그인

eslint-plugin-prettier: 린트 위에 사용할 프리티어 플러그인 --> ESLint와 prettier를 통합, prettier의 코드 스타일 규칙을

ESLint로 검사할 수 있도록 해준다. prettier 포매팅 규칙을 ESLint의 linting 작업 중에 자동으로 검사할 수 있다.

 

 

eslint-config-prettier: 린트 설정(eslint)과 중복되는 부분이 있으면 프리티어 룰에서 제외하는 플러그인

---> eslint에는 문법과 문제를 찾는 설정만 남겨놓고, prettier에는 코드 스타일링만 남겨놓도록

@typescript-eslint/elint-plugin: 타입스크립트 전용 린트

 

모듈 설치, eslint파일 init하는 것만으로는 eslint의 내용이 완벽하게 작성되지 않음

개발자가 eslint의 파일을 수동으로 작성해야 하는 부분이 있다.

 

eslint파일 구성 확인

 

eslint파일 내에 작성된, export default [...] 구문을 사용해야하는 eslint 버전의 기준은 8.0.0  

CommonJS 모듈 시스템을 사용하는 .eslintrc.js 파일: module.exports 방식으로 작성

eslint.config.mjs는 ECMAScript Modules 방식으로 export default 방식으로 작성해야 한다. 

 

eslint가 자바스크립트의 문법과 오류를 검출하려면 기본적으로 자바스크립트를 parser해야하고,

해당 parser를 무엇을 사용할 것인지 eslint.config.mjs에 작성해야함.

parser와 paserOptions은 languageOptions내에 작성해야 함

 

eslint.config.mjs 파일

import globals from 'globals';
import pluginJs from '@eslint/js';
import tseslint from '@typescript-eslint/eslint-plugin';
import pluginReactConfig from 'eslint-plugin-react/configs/recommended.js';

export default [
  // 파일에 대한 glob 패턴
  { files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'] },
  {
    languageOptions: {
      parser: '@babel/eslint-parser', // Babel 파서 사용
      parserOptions: {
        requireConfigFile: false, // Babel 설정 파일 없이 사용 가능
        ecmaVersion: 2023, // 최신 ECMAScript 버전 사용 (2020)
        sourceType: 'module', // ES 모듈 사용
        ecmaFeatures: {
          jsx: true, // JSX 문법 사용
        },
      },
      globals: globals.browser, // 브라우저 환경에 맞는 글로벌 변수 설정
    },
  },
  // ESLint 기본 설정
  pluginJs.configs.recommended,
  // TypeScript 관련 설정
  ...tseslint.configs.recommended,
  // React 관련 설정
  pluginReactConfig,
  {
    rules: {
      // Prettier와 ESLint 규칙을 통합
      'prettier/prettier': 'error', // Prettier 규칙을 위반하면 오류 처리
      // React PropTypes 사용하지 않으면 off
      'react/prop-types': 'off',
    },
  },
  // Prettier 관련 설정 (필요한 경우 추가)
  {
    extends: [
      'eslint:recommended', // 기본 ESLint 권장 규칙
      'plugin:react/recommended', // React 관련 권장 규칙
      'plugin:prettier/recommended', // Prettier 규칙 적용
      'prettier', // ESLint와 Prettier 충돌 해결
    ],
    plugins: [
      'react', // React 플러그인
      'prettier', // Prettier 플러그인
    ],
  },

  {
    env: {
      // 실행 환경 설정
      browser: true, // 브라우저에서 실행되는 코드임을 설정
      es2023: true, // ECMAScript 2023 문법 사용을 설정
      node: true, // Node.js 환경에서 실행될 코드도 포함
    },
  },
];

 

.prettierrc

{
  "printWidth": 120,
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": true,
  "trailingComma": "all",
  "bracketSpacing": true,
  "jsxBracketSameLine": true,
  "vueIndentScriptAndStyle": false,
  "endOfLine": "auto"
}

 


리액트 프로젝트 구조 및 코드분석

 

- The react-dom/client APIs는 클라이언트 브라우저에서 리액트 컴포넌트를 렌더한다.

react-dom/client APIs는 전형적으로 리액트 트리(react-tree)를 초기화하기 위해 어플리케이션의 가장 높은 레벨에서 사용된다.

프레임워크가 react-dom/client APIs를 호출할 수 있다. 대부분의 컴포넌트들이 react-dom/client를 import하거나 사용할 필요가 없다.

 

Client APIs

createRoot: 브라우저 DOM node 내부에서 리액트 컴포넌트를 보여줄 root를 생성하게 해준다.

 

public폴더내의 index.html 파일에서 <div id="root"><div> id가 root인 div태그를 얻고,

해당 div태그 안에 클라이언트에게 보여줄 화면을 리액트 컴포넌트로 구성하여 렌더링한다.

src폴더의 index.js에서

const root = ReactDOM.create(document.getElementById('root')); // index.html 파일에서 id가 root인 요소를 얻고,

// 리액트 어플리케이션을(리액트 컴포넌트들) 렌더링할 루드 DOM 노드를 만들어낸다.

// 리액트 컴포넌트들로 화면을 설계하는데 도면과 같은 역할을 하는 것이 DOM 노드인듯

// 그리고 DOM노드를 클라이언트 브라우저 화면에 렌더한다.

root.render(

  <App />

);

 

<React.StrictMode>

StrictMode는 개발 중에 컴포넌트에서 일반적인 버그를(common bugs) 빠르게 찾을 수 있도록 한다.

Use StrictMode to enable additional development behaviors and warnings for the component tree inside:

컴포넌트 트리 내부에서 발생하는 경고와 추가적으로 개발하는 행동을 가능하게 하기 위해 StrictMode를 사용하세요.

enable: a feature of a computer to be active or available for use: 사용을 위해 활성화되거나 사용할 수 있게 되는 컴퓨터의 기능

React.StrictMode를 사용하면 개발하는 환경에서(코드를 실행하는 환경이 아닌) 미리 버그들을 발견할 수 있다.

 

import { StrictMode } from 'react';

// 상기와 같이 react에서 StrictMode를 import해서 컴포넌트를 바로 호출할 수 있고,

// 예를 들면 <StrictMode> 와 같이,

 

// 아니면 다음과 같이 StrictMode에 접근할 수 있다.

import React from 'react';

<React.StrictMode>

    <App />

</React.StrictMode>


카카오 소셜 로그인 개요 공부

카카오 로그인은 OAuth 2.0 기반의 소셜 로그인 서비스다.

OAuth 2.0 이란, 인증(Authentication)을 통해서 허용받은 공간의 자원을 사용할 수 있다(인가, Authorization)

OAuth의 핵심은 인증과 인가다. OAuth에서 Resource Server는(리소스/지원을 갖고 있는 서버는)

Resource Owner(리소스의 소유자)에게 인증을 받음으로써 해당 Client(리소스가 필요한 사용자)가

Resource에 접근할 수 있는지 확인한다.

 

구글에 가입하여 구글 계정이 있고, 해당 계정으로 구글 이메일을 이용할 수 있는

사용자의 이름과 이메일에 OAuth에 따라 접근하는 과정

 

커뮤니티 서비스를 Client로, 커뮤니티 서비스를 사용하려는 사용자를 Resource Owner(리소스의 소유자)

그리고 Resource Server(리소스/자원을 갖고 있는 서버, Service Provider)인 구글이 있다고 했을 때,

 

커뮤니티에서 구글 로그인을 통해 홈페이지에 접속가능한 기능을 제공한다고 했을 때,

우선 커뮤니티는 Service Provider에 자신의 신원을 등록해야 한다 → 1. 커뮤니티는 Service Provider에 Client로 등록해야 한다.

2. 신원을 등록한 후에 Service Provider는 Client별로 식별할 수 있는 Client Id와 Secret Key가 생성해 준다.

이 Id와 Key는 Client를 식별하고, Client의 신원을 확인하는데 사용한다.

 

***------------------------------------------------------------------------------------

사용자가 카카오 로그인 버튼을 눌렀을 때(작업 흐름)

 

사용자가 클라이언트의 사이트에서 카카오 소셜 로그인 버튼을 눌렀을 때,

카카오 서버에 요청이 가고, 카카오 서버는 카카오 로그인 페이지를 응답한다.

로그인 페이지에 아이디와 비밀번호를 입력하고 로그인 버튼을 누르면,

사용자의 아이디와 비밀번호는 카카오 서버로 전송된다.

 

카카오 서버에서 사용자의 아이디와 비밀번호가 일치하는지 확인

사용자의 아이디와 비밀번호가 일치할 경우 클라이언트 사이트에 redirect URL로

카카오 로그인 서비스를 신청한 클라이언트가 맞는지 client Id와 Seceret Key를 대조해 확인한다.

└ 카카오 서버가 client에게 신원 확인을 요청함. 이 신원 client Id와 Secret Key로 확인해야함

api-key로 인증받아야 함. key가 맞다고 판단이 되면 client에게 사용자 정보에 접근할 수 있는 access key를 전달한다.

그리고 client는 access key로 user(사용자)의 정보에 접근할 수 있다. 구글에게 사용자의 정보를 달라고 하는 것

 

사용자가 카카오 로그인 버튼을 눌렀을 때(코드 흐름)

 

Login.js에 카카오 로그인 버튼 이미지를 배치할 것이고,

카카오 로그인 버튼 이미지를 클릭하면 라우터를 통해서

카카오 로그인 핸들러가(components/user/KaokaoLoginHandler.js) 동작할 수 있도록 처리할 것임

 

인가코드를 받는 것까지 리액트로 진행해보기

인가 코드를 받는 과정은

- 카카오 서버가 카카오 로그인 페이지(id와 pw 입력 페이지)를 사용자 브라우저로 응답하고,

- 사용자가 id와 pw를 입력한 후 카카오 서버에 전송하면 카카오 서버에서는 id와 pw가 일치한지를 검증한 후

- 일치한다면 사용자 브라우저에 카카오 로그인 동의 화면을 호출하고, 사용자가 동의 항목에 체크하고, 동의하기 버튼을 누르면

- 그 정보 또한 카카오 서버에 전송되고, 카카오 인증 서버는 인가받은 동의 항목 정보를 담은 인가코드 발급받고,

- 카카오 인증 서버는 웹 사이트가 등록한 redirect url로 인가코드를 전송할 것이다. 

 

인가코드를 받기 위해 웹 사이트에서 요청해야할 url과 url을 통해 같이 보낼 쿼리 스트링 설정

1. 인가 코드를 받기 위해 요청보낼 url: https://kauth.kakao.com/oauth/authorize

2. 요청보낼 url에 같이 전달해야할 쿼리스트링: client_id, redirect_uri, response_type --> 이 3개가 필수다.

 

조합해보면 https://kauth.kakao.com/oauth/authorize?client_id=카카오개발자에서발급받은ClientId&redirect_uri=등록한redirecturl주소&response_type=code

 

이 조합된 url을 카카오 로그인 버튼을 눌렀을 때 요청이 갈 수 있게끔 설정한다.

 

Kakao-config.js

 

Login.js

 

라우트(Route)를 통해 설정하기

그리고 설정한 redirect_url로 카카오 인증서버가 인가코드를 보내면

KakaoLoginHandler.js에서 받을 수 있게 코드를 작성하겠다.

/oauth/kakao ---> 내가 설정한 redirect url로 카카오 인증서버가 인가코드를 보내주는데,

카카오 인증 서버가 redirect url인 '/oauth/kakao'로 요청이 들어오면(이때 인가코드를 실어옴)

KaokaoLoginHandler 컴포넌트를 호출하도록 설정

 

<Routes>

    <Route path= '/oauth/kakao' element={<KakaoLoginHandler />} />

<Routes>

url의 값이 /oauth/kakao로 들어오면 라우터가 감지해서 KakaoLoginHandler 컴포넌트를 렌더링한다.

 

카카오 인증 서버가 redirect url로 인가코드를 보내는데,

그 인가코드를 얻기 위해 다시 redirect url로 요청을 보내서 받아야 한다.

 

사용자가 카카오 로그인 후, 동의 화면에서 동의 항목에 체크를 한 후, 동의하기 버튼을 눌렀다면

카카오 인증 서버에서 내가 설정한 redirect url로 인가코드를 쿼리스트링으로 응답한다고 한다.

 

카카오 서버가 redirect url로 전송한 인가코드를(${AUTHORIZE_CODE}) 얻기 위해 

현재 url의 값을 얻고, 해당 url에서 code의 값을 얻어보자

 

KakaoLoginHandler.js

const urlInfo= new URL(window.href.location);console.log('urlInfo: ', urlInfo);

 

현재 url주소관련 정보를 가진 객체 urlInfo에서 쿼리스트링인 code에 대응하는? 값을 얻으려면 searchParams.get('code') 

urlInfo.search는 ?(물음표)를 포함한 전체 쿼리스트링을 가져오고,

searchParams로 얻은 URLSearchParams 객체?에서 get() 메서드를 사용하여_매개변수로 이름(key) 전달,key에 대응하는? value를 얻을 수 있어 인가코드에 이렇게 접근할 수 있다.

 

// url에 쿼리스트링으로 전달된 인가 코드를 얻어오는 방법const code = new URL(window.location.href).searchParams.get('code');

 

그리고 이 인가코드는 액세스토큰을 발급받는데 필요하며,

발급받은 액세스토큰을 사용하여 사용자의 정보를 얻어낼 수 있다.

 

이 인가코드를 백엔드로 전달하기(수업에서는 백엔드로 전달했지만, 개인적으로 리액트로 진행해보겠다)

토큰 받기

- 인가코드로 토큰 발급을 요청한다. 웹 사이트에서 카카오 인증 서버에게 POST방식으로 인가코드와 함께 하기 url로 요청한다.

액세스 토큰을 요청할 url: https://kauth.kakao.com/oauth/token

- 토큰 발급 요청 성공시 응답은 토큰과 토큰 정보를 포함한다.

**인가코드 발급만으로는 카카오 로그인이 완료되지 않음, 토큰 받기까지 마쳐야 카카오 로그인을 할 수 있음

발급받은 토큰 정보로(액세스 토큰) 사용자 정보에 접근할 수 있다. 

 

POST 요청시 필수 파라미터 확인하기

 

 

 

카카오 개발자 센터에 인가코드로 토큰을 요청하는 POST 예제가 하기처럼 작성되어 있는데, 앞에 표식을 몰라서 공부해보았다.

curl은 Client URL의 줄임말, 즉 카카오 서버에게 액세스 토큰 요청을 하기위해 내가(웹 사이트가) 요청보내야할 base url?을 의미,

-v는 verbose 모드로, 요청 및 응답의 자세한 정보를 출력한다고 한다. -X는 HTTP 요청 방법을 지정한다. -X POST는 요청 방법(방식/메서드)을 POST로 하겠다는 의미, -H는 Header를 지정하는 옵션이다. http요청에는 헤더라는 부분이 포함되는데, 요청에 대한 메타정보를 서버에 전달하는 역할을 한다. 예를 들어 요청하는 데이터의 형식, 인증 정보, 클라이언트의 정보 등을 헤더에 포함시킨다. 

-H 옵션은 HTTP 요청 헤더를 추가하는데 사용된다. 인가코드를 사용해 액세스토큰을 요청하는데 헤더에 콘텐츠 타입을 지정할 것을 필수로 두었기 때문에 Content-Type은 반드시 추가해야하는 항목이다. 그리고 본문에 필수로 포함해서 보내야할 파라미터, grant_type, client_id, redirect_uri, code(인가코드)를 순서대로 나열한 것이다. 

 

``` 인가코드로 액세스토큰을 받아오는 과정에서 에러발생

POST https://kauth.kakao.com/oauth/token 400 (Bad Request)

KaokaoLoginHandler.js:30 data:  {
  "error": "invalid_request",
  "error_description": "request parameters not found.",
  "error_code": "KOE001"
}

```

 

요청 매개변수를 못찾겠다는 건데, 요청할 때 필수 매개변수인

redirect url 값을 인코딩해서 전달해야 하는 사항을 발견하게 되었다.

redirect url값을 인코딩해서 다시 전달해보겠다.

encodeURI()함수를 사용해서 기존의 redirect_url 값을 인코딩한뒤 값을 바꾸어서 다시 전달했는데

이번에는 invalide grant 에러가 떴다. 유효하지 않은 권한에 대해 검색해보니 redirect url과 인가코드의 유효성? 문제가 있었다.

redirect url에는 문제가 없고, 인가코드의 문제인가 싶어 리액트 프로젝트를 종료하고 다시 실행시켰는데,

이번엔 성공적으로 액세스토큰이 콘솔에 출력됐다.

 

발급받은 액세스토큰을 갖고 사용자 정보에 접근하기

액세스토큰 요청에 카카오 서버가 응답함. 응답된 데이터를 확인해보자.

- token_type, 문자열 타입, 값은 bearer로 고정

- access_token, 문자열 타입, 사용자의 액세스토큰이다. 이 값으로 사용자의 정보에 접근할 수 있다.

- expires_in, 정수타입, 액세스토큰(과 ID 토큰의) 만료 시간(초단위)

- refresh_token, 문자열 타입, 사용자 리프레시 토큰 값

- refresh_token_expires_in, 정수타입, 사용자 리프레시 토큰 만료시간(초단위)

- scope, 문자열 타입, 인증된 사용자의 정보조회 권한 범위를 의미하고, 범위가 여러 개일 경우, 공백으로 구분한다.

 

액세스 토큰 요청으로 응답받은 데이터 분석

리프레시 토큰 값의 만료시간이 액세스토큰 값의 만료시간보다 더 길었다.

사용자의 정보조회 권한 범위는 이메일(account_email), 프로필 이미지(profile_image), 프로필 닉네임(profile_nickname)

 

이제 발급받은 액세스토큰으로 사용자 정보에 접근하기

 

사용자 정보를 요청하는 url: https://kapi.kakao.com/v2/user/me

GET방식으로 헤더정보에 액세스토큰을 전달하여 응답받은 데이터를 콘솔에 찍어봤는데

하기와 같은 정보를 얻을 수 있었다.

{
  "id": 노출방지를 위한 수동처리,
  "connected_at": "2024-11-21T06:46:42Z",
  "properties": {
    "nickname": "노출방지를 위한 수동처리",
    "profile_image": "노출방지를 위한 수동처리",
    "thumbnail_image": "노출방지를 위한 수동처리"
  },
  "kakao_account": {
    "profile_nickname_needs_agreement": false,
    "profile_image_needs_agreement": false,
    "profile": {
      "nickname": "노출방지를 위한 수동처리",
      "thumbnail_image_url": "노출방지를 위한 수동처리",
      "profile_image_url": "노출방지를 위한 수동처리",
      "is_default_image": true,
      "is_default_nickname": false
    },
    "has_email": true,
    "email_needs_agreement": false,
    "is_email_valid": true,
    "is_email_verified": true,
    "email": "노출방지를 위한 수동처리"
  }
}

 

 

KaokaoLoginHandler.js --- 인가코드 얻기, 액세스토큰 얻기, 액세스토큰으로 사용자 정보 얻기_흐름을 알기위해 원스탑으로 진행

// rafce
import React, { useEffect } from 'react';

const KaokaoLoginHandler = () => {

  console.log('사용자가 동의화면을 통해 필수 정보 동의 후 Kakao 인증서버에서 redirect를 진행함');

  // url에 쿼리스트링으로 전달된 인가코드를 얻어오는 방법
  const code = new URL(window.location.href).searchParams.get('code');
  console.log('code: ', code);

  // KakaoLoginHandler 컴포넌트가 렌더링될 때
  // 인가코드를 백엔드로 전송하는 fetch 요청하기
  useEffect(() => {
    const clientId = process.env.REACT_APP_REST_API_KEY;
    const encodeRUrl = encodeURI(process.env.REACT_APP_REDIRECT_URL);

    const tokenRequestUrl = 'https://kauth.kakao.com/oauth/token';

    console.log(
      `토큰 url 확인: grant_type=authorization_code&client_id=${clientId}&redirect_uri=${encodeRUrl}&code=${code}`,
    );

    const kakaoLogin = async () => {
      const response = await fetch(tokenRequestUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: `grant_type=authorization_code&client_id=${clientId}&redirect_uri=${encodeRUrl}&code=${code}`,
      });

      const data = await response.json();
      // console.log('data: ', data);

      // const tokenInfo = JSON.stringify(data, null, 2);
      // const token = data.access_token;
      // console.log('token: ', token);
      return userAccessTokenInfo(data);
    };

    const userAccessTokenInfo = async (data) => {
      // console.log('data: ', data);
      // const token = data.access_token;

      const response = await fetch('https://kapi.kakao.com/v2/user/me', {
        method: 'GET',
        headers: { Authorization: `Bearer ${data.access_token}` },
      });

      const userInfo = await response.json();
      console.log(JSON.stringify(userInfo, null, 2));
    };

    kakaoLogin();
  }, []);

  return (
    <>
      <h1>KaokaoLoginHandler</h1>
    </>
  );
};

export default KaokaoLoginHandler;

 

```

useEffect는 외부 시스템과 컴포넌트를 동기화하는 React Hook(리액트 훅)이다.

synchronizes components with external systems --- 외부시스템?

외부시스템(예를 들면 API, 데이터베이스, 브라우저 로컬 저장소, 타이머 등)과 통신하여

그 상태를 반영하거나 업데이트하는 작업을 한다는 의미다.

React에서는 컴포넌트가 렌더링될 때, 외부 시스템에서 데이터를 가져오거나,

useEffect 훅은 컴포넌트가 렌더링된 후 실행되도록 하여 동기화를 돕는 역할을 한다.

브라우저의 상태(ex. 로컬 스토리지에 저장된 정보)를 불러오는 등의 작업을 할 수 있다. 

 

```


 

스프링 부트에서 소셜 로그인할 때는 api-key를 application.properties 파일에 작성하기

리액트 프로젝트에서는 .env파일에 api-key를 보관하기

application.properties, .env파일에 작성되는 내용들

- 데이터베이스 url, username, password와 같이 개인정보들이 작성된다.

.env 파일 생성 및 .gitignor에 .env 추가하기

.env 파일에 데이터 작성할 때 지켜야하는 규칙

1. React에서 변수명은 REACT_APP_으로 시작되어야 한다.

2. 변수명과 값 사이에는 공백 또는 따옴표("")가 존재해서되는 안된다.

3. 변수명은 대문자로 작성하는 것이 관례다.

 

컴포넌트에서 .env에 저장한 변수명에 접근하는 방법은

const 변수명 = process.env.REACT_APP_내가저장한변수명;

과 같이 작성하여 변수명으로 접근할 수 있다.

 

***기본주소/login으로 요청했을 때 로그인 페이지 나오게 하기 --> 라우터와 관련있는 듯

React Router 설정

리액트에서 화면 전환할 때 <a>태그가 아닌 <Link> 컴포넌트를 사용한다.

리액트는 SPA(Single Page Application)라 화면을 전환한다?는 어색한 개념이다.

리액트 라우터를 사용해서 화면이 전환되는 느낌을 준다. 화면이 전환되는 것은 아니다.

사용자가 봤을 때는 다른 페이지로 전환된 것처럼 보이지만 실제로는 다른 페이지로 전환된 것이 아니라

url을 보고 라우터가 리액터한테 경로가 바뀌었다고 알려주는 것이다.

그래서 경로에 맞는 새로운 컴포넌트가 렌더링될 수 있게 처리된다.

<Link /> 컴포넌트는 단독으로 사용이 불가하다: import { Link } from 'react-router-dom';

Link컴포넌트를 사용하려면 설정이 필요하다.

Link 컴포넌트를 단독으로 사용했을 때 하기와 같이 에러 발생

```

Uncaught TypeError: Cannot destructure property 'basename' of 'react__WEBPACK_IMPORTED_MODULE_0__.useContext(...)' as it is null.

 

The above error occurred in the <Link> component:

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.

```

리액트에서 라우터를 사용하는 이유

라우터라는 단어는 네트워크 통신에서도 사용함.

리액트에서 라우터는 단일 페이지간의 어플리케이션에서 다른 페이지간의 전환과 URL 관리를 가능하게 하기 위해서다.

라우터를 사용하면 사용자가 어플리케이션 내에서 다른 경로로 이동할 때, 페이지를 새로고침하지 않고도 컴포넌트를 렌더링하고,

해당 경로를 url에 반영할 수 있다. react-router-dom을 설치하면 리액트 라우터를 사용할 수 있다.

 

index.js 에서 root.rent() 메서드로 <App /> 컴포넌트를 렌더링하는데,

<App /> 컴포넌트에서는 <Header />와 <Footer /> 컴포넌트를 포함하고 있고,

<Header /> 컴포넌트에서는 로그인 페이지로 이동하는 Link컴포넌트를 호출하고 있다.

index.js에서 <App /> 컴포넌트를 BrowserRouter컴포넌트로 감싸고 → import { BrowserRouter } from 'react-router-dom';

<App />컴포넌트에서 Routes컴포넌트를 호출한다. Routes컴포넌트는 Route컴포넌트를 둘러싸고 있는데,

Route컴포넌트는 path와(어떤 url이 호출됐을 때) element(렌더링될 element)를 작성한다.

 

index.js

 

App.js

 

Header.js

 

Link컴포넌트를 사용하기 위해서 BrowserRouter컴포넌트와 Routes, Route컴포넌트 사용이 필요하다.

 

```

개발 과정에서 API_KEY, DB관련 정보 등 외부로 노출되면 안되는 데이터들이 있는데,

.env 파일을 통해 내부적으로만 사용할 수 있게 설정할 수 있다. (.env --> 환경 변수 설정을 위한 파일)

.env 파일 내의 값을 읽어올 때는 [process.env.지정한이름] 을 통해 값을 불러올 수 있다.

React 환경에서 .env 내의 데이터를 읽어올 때는 반드시 REACT_APP_을 붙여주어야 한다.

.env파일 내에 REACT_APP_을 붙이고, 변수명을 작성하지 않고, 변수명만 작성해도 되지만,

리액트 환경에서 .env 내의 데이터를 읽어올 때는 반드시 REACT_APP_을 붙이고, 변수명을 붙여야 해당 값을 불러올 수 있다.

 

```


Spring: 카카오 로그인 서비스 신청 및 인가 코드 요청:

https://www.youtube.com/watch?v=1tnBT7KL-gg&list=PLiqgNGl0CcSazWnIe8ld17GwRC70mQ8VD&index=342

 

라우터관련 강의, 26:33부터,  https://youtu.be/viTCBMJzE9M?list=PLiqgNGl0CcSazWnIe8ld17GwRC70mQ8VD&t=1593

 

설치한 패키지

npm install @mui/material @emotion/react @emotion/styled

참고

1. React 기초 1강 : 리액트 설치와 셋팅법 (2022+ 스타일): https://www.youtube.com/watch?v=nahwuaXmgt8

2. 아직도 React 폴더 구조로 고민하고 계신가요? FSD 한 번 써보세요[제로초뉴스]: https://www.youtube.com/watch?v=64Fx5Y1gEOA

3. FSD 관련 자료: https://emewjin.github.io/feature-sliced-design/?utm_source=substack&utm_medium=email

4. 카카오 개발 문서: https://developers.kakao.com/docs/latest/ko/kakaologin/js#req-user-info