API 문서는 무엇인가?

  • 백엔드 개발자가 API를 설계한 방식을 적어두어 어떻게 사용하면 되는지 적어둔 문서
  • API 설명서

API 문서는 왜 필요한가?

1. 일관성

2. 협업과 유지 보수

3. API 사용도 중 일어난 문제해결 및 디버깅

 

API에 필히 포함되어야 하는 정보

  • URL 주소 / HTTP 메서드
  • API 설명
  • 요청 형태
    • Path Parmas
    • Query Params
    • Body Params
    • 필수 여부
    • 데이터 타입
  • 응답 형태
    • 응답 코드(status code)
    • 각 응답 코드별 설명
    • 응답 형태 (response value)
    • 응답 값에서 각 파라미터의 의미
    • 응답 값에서 각 파라미터의 필수 여부
    • 응답 값에서 각 파라미터의 데이터 타입

 

이런 이런경우 메세지에 있는 포트 번호를 이용하여 사용여부를 확인

Error: listen EADDRINUSE: address already in use ::: 3000

lsof -i:3000

위 코드를 터미널에 입력하면 아래와 같은 결과를 보여준다.

 

여기서 PID 번호를 이용해서 사용 중인 서버 종료시키기

kill 10287

 

목표

회원가입, 로그인, 회원조회 기능 구현

JWT 를 이용해보기

 

구현 시 몰랐던 것들

1. 정규표현식 : email validation check 를 @ 가 빠졌는지 공백있느지를 체크하기

  • split() 을 이용해서 확인이 가능하지만, 이를 정규표현식을 이용해서 구현해보려고 함.
  • 구글링을 통해서 email을 체크하는 정규표현식 찾음
/^([0-9a-zA-Z_\.-]+)@([0-9a-zA-Z_-]+)(\.[0-9a-zA-Z_-]+){1,5}$/
  • 위 표현식으로 하면 이메일을 기입할 때 이 메일 주소의 시작이 -,  _ , . 을 사용해도 유효하다고 판단하게 된다는걸 확인.
    ex)"-test@email.com" 
/^[0-9a-zA-Z]([0-9a-zA-Z_\.-]+)@([0-9a-zA-Z_-]+)(\.[0-9a-zA-Z_-]+){1,5}$/;
  •  의미
    1. /^ : 시작
    2. 첫문자는 [0~9, a-z, A-Z] 에 있는 문자로 시작 
    3. 다음 구간 ( ) 은 입력된 각각의 문자가 조건 [0~9, a-z, A-Z, _, \.(마침표) , -] 과 매칭되어야한다.   
    4.  [ ]+ 의미는 받은 문자에서 주어진 구간 안에 있는 각각의 문자를 앞에  [ ]안에 주어진 표현과 맞는지 모두 체크한다.
    5.  @ 가 있어야 한다
      • @ 으로 표현하면 반드시(*) @가 있어야 한다고 강조가능
    6. ( )에 있는 문자들은 [0~9, a-z, A-Z, _, -]+ 에 각각의 문자가 매칭되어야 한다. 
    7.  다음 구간에서는 . 가 먼저 있어야하고
    8. 그 다음의 문자들은 [0~9, a-z, A-Z, _, -]+  에 각각의 문자가 매칭되어야한다.
    9. {1, 5} 앞과 같은 패턴이 1번에서 5번까지 반복될 수 있다.
      • (ex) longest.email.domain.i-ve.ever.seen
        • 1 번 : longest.email
        • 2 번 : email.domain
        • 3 번 : domain.i-ve
        • 4 번 : i-ve.ever
        • 5 번 : ever.seen 
    10. $/  : 문자 끝.

2. 로그인 기능 구현 시 HTTP Method 선택

  • 첫번째 선택 : GET
    • 선택한 이유 : '로그인' 기능에서 유저의 아이디와 비밀번호가 일치하는 지 조회를 해서 확인다고 생각함.
    • 틀린 이유 : GET method의 의미는 '조회' 이다.  API 입장에서는  '로그인' 이 데이터를 조회하는 것과 거리가 멀다고 판단되어진다.
    • 올바른 선택  : POST HTTP Method

3. HttpException 활용

  • 미들웨어(middleware) 활용법 : 자료출처(Express.com)
  • next() 의 역활
var express = require('express');
var app = express();

var requestTime = function (req, res, next) {
  req.requestTime = Date.now();
  next();
};


app.get('/',requestTime, function (req, res) {
  var responseText = 'Hello World!';
  responseText += 'Requested at: ' + req.requestTime + '';
  res.send(responseText);
});

app.listen(3000)
  1.  requestTime 이라는 미들웨어가 있다고 가정, 여기서 현재시간을 req.requestTime에 담는 기능을 한다.
    • 여기서 next() 의 역활이 req 에 담겨있는 값을 다음 함수로 넘겨준다.
  2.  이 미들웨어를 app.get() 에 사용하면 
    1. function 에 있는 req 에 requestTime에서 전달해주는 현재시간 정보 (req.requestTime)가 담겨지게 된다.
    2. 받은 현재 시간 정보를 이용하여 원하는 코드에 사용가능

3. 코드의미

const token = autherHeader && autherHeader.split(' ')[1];
  •  autherHeader 가 undefined 이면 false 를 반환
  • autherHeader 에 값이 있으면 && 뒤에 코드를 수행

4. 주어진 기능 수행 중 발생한 에러를 응답으로 해야하는 이유

  •  프론트앤드 개발자와 같이 API를 사용해도 프론트앤드 개발자는 콘솔에 나오는 에러를 볼 수가 없다. 그러므로 에러를 Response의 값으로 어떤 에러가 났는지 응답해줘야 한다.

에러발생

  • database 연결할 때 데이터베이스 이름만 적어서 오류를 발생시킴
  • 오류 : Error: secretOrPrivateKey must have a value
    • 오류 발생원인 유추
      • 1. dotenv 를 설치하지 않아지 않았다. ->  설치 된 것 확인
      • 2. .env 에 해당하는 값이 없다. -> 키와 값 확인
      • 3. 해당 코드가 올바르지 않다. -> 확인 올바름 jwt.sign(,,,,)
      • 4. 변수 값 확인 -> 'process.env.JWT_SECRET_KEY' 를 체크 -> undefined
        • .env 에 값이 있는데 왜 undefind 일까... -> 공식문서 확인 ->....설치된 라이브러리를 호출 안함...
    • 원인: 설치된 라이브러리 호출 안함 
      • 라이브러리 호출시 항상 코드의 제일 상단에 할것!!

구현 시 어려웠던 것들

알고 있는 데 계속 기억이 잘 안났던 점

  • Postman 으로 JWT 테스트 할때는 'Bearer' 추가하고 JWT access token 입력
  • JavaScript 에서는 camel 스타일을 사용 하므로 이를 MySQL로 옮길때 이를 underscore 타입으로 변형시켜주는 옵션
    • model 을 설정 다한 후  ,{ underscored:true }  추가!!

'프로그래밍 > Node.js' 카테고리의 다른 글

Swagger 사용방법  (0) 2023.11.30
API 문서를 작성하는 이유  (0) 2023.11.29
비밀번호 암호화하기 기본(단방향)  (0) 2023.11.18
JWT 기본 개념/인증,인가  (0) 2023.11.07
[Sequelize] DB연결 에러  (0) 2023.08.07

비밀번호 암호화 하는 이유

DB에 나의 비밀번호가 암호화 하지 않고 그대로 저장을 하게 되면,

해커가 DB를 통해서 나의 비밀번호를 알게 되고 나의 계정을 마음대로 사용할 수 있게 되버린다.

이를 막기위해서 비밀번호를 암호화를 한다.

 

비밀번호 암호화의 종류

  • 양방향 암호화: 암호화된 데이터에 대한 디코딩(decoding)이 가능한 암호화 방식.
    (ex)
    대칭키, 공개키 <자세한건 나중에 정리>
    • 디코딩 : 부호화(encoding)된 암호를 부호화 되전의 상태로 돌려 사람들이 읽을 수 있도록 하는 것
  • 단방향 암호화 : 암호화된 데이터에 대한 디코딩이 불가능한 암호화 방식.
    (ex)
    Hash, MAC <자세한건 나중에 정리>

비밀번호 암호화하는 과정 ( 단방향 암호화방식)

설치 및 생성

// express 프로젝트 생성
npm i init
npm i express

// 암호화 라이브러리 설치
npm i bcrypt

* bcrypt:  hash 방식으로 암호화하는 것을 도와주는 라이블러리,  password-hashing function

hasing 이란 → 주어진 값을 다른 값으로 변형시키는 것이다. 이때  사용되는 수학적 알고리즘에 따라 변형된 값이 생성된다. 

코드 예시

const express = require('express');
const bcrypt = require('bcrypt');

const app = express();
app.use(express.json());
const port = 3000;

const users = [];

// 회원가입
app.post('/sign-up', async (req, res) => {
  const { username, password } = req.body;

  // 비밀번호 해시화
  const saltRounds = 10; // salt: 설정한 password를 복잡하게 변경시키는 것, 이것을 10번 실행
  const hashedPassword = await bcrypt.hash(password, saltRounds);

  // 임시로 사용자 정보 저장
  const newUser = {
    username: username,
    password: hashedPassword,
  };
  users.push(newUser);

  return res.status(201).send(newUser);
});

// 로그인
app.post('/sign-in', async (req, res) => {
  const { username, password } = req.body;

  // 등록된 사용자인지 확인 
  const user = users.find(user => user.username === username);
  if (!user) {
    return res.status(401).send({ message: '등록되지 않은 사용자입니다.' });
  }

  // 저장된 해시화된 비밀번호와 입력된 비밀번호 비교
  const isPasswordValid = await bcrypt.compare(password, user.password);
  if (!isPasswordValid) {
    return res.status(401).send({ message: '비밀번호가 일치하지 않습니다.' });
  }

  // Access Token 발급 로직으로 교체하기
  return res.status(200).send({ accessToken: "ACCESS_TOKEN" });
});

app.listen(port, () => {
  console.log(`앱이 http://localhost:${port} 에서 실행 중입니다.`);
});

 

 

JWT(JSON  Web Token)?

인증에 필요한 방식으로 특정유저가 로그인 할 때, 정말로 인증된 사용자인지 검증할 때 사용.

로그인한 정보가 정말로 인증된 사용자인지 검증할때 사용

  • 사용예시
    • 인증된(로그인한) 유저만 게시글을 작성 -> 특정회원으로 로그인 됨을 증명 
    • 인증된 관리자만 데이터베이스에 접근 -> 관리자로 로그인 됨을 증명

장점 / 단점 

  • 장점
    • 가볍고 클라이언트 어플리케이션에서 사용하기 쉽다 (예) 모바일 어플리케이션
    • 토큰에 모든정보가 들어가 있어서 토큰을 이용해서 인증(검증)이 가능하다.
      • 4가지 단계의 인증절차
        • 요청(클라이언트,사용자) -> 확인(서버) -> 토큰(서버가 사용자를 확인하고 토큰발급) -> 저장(사용자의 브라우저에 저장)
    • 토큰 만료 시스템이 있다.
  • 단점
    • 단일 키를 이용하여, 노출되면 시스템 전체가 위험에 노출된다.
    • 토큰이 복잡하여 이해하기가 쉽지 않다.
      • 개발자가 암호서명 알고리즘에 정통하지 않으면 자신도 모르게 시스템을 위험에 빠뜨릴 수 있다.
    • 메세지를 모든 클라이언트에게 푸쉬 불가능, 서버 측 클라이언트도 관리할 수 없다 

구성

  • 구성 -> 토큰을 받으면 https://jwt.io/ 에서 아래 처럼 디코드 된 것을 확인 가능
  • eyJhbGciO.I6IkpvaG4gRG2MjM5MDIyfQ.POk6yJV_adQssw5c
    • Header.Payload.Signature
/* Header (알고리즘, 토큰 타입)*/
{
    "alg": "HS256", // 서명(signature) 생성 시 사용한 해시 알고리즘 : HS256 지원
    "typ": "JWT" // 토큰 타입 : < JWT 만 지원>
}

/* Payload (내용, 데이터) */
{
  "id": 1,
  "iat": 1699358134, // 토큰 발행 시간
  "exp": 1700222134 // 토큰 만료 시간 (옵션)
}

/* VERIFY SIGNATURE */
/* header와 payload에 입력된 값을 암호화 하고
 secret 부분은 검증(보안서명을 통해 메세지가 전송 과정에서 바뀌지 않은 것을 확인)*/
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret
)

 

 

인증/ 작동 과정

 

  • 요청(클라이언트,사용자) -> 확인(서버) -> 토큰(서버가 사용자를 확인하고 토큰발급) -> 저장(사용자의 브라우저에 저장)

 

사용이유

  • 임시 엑세스를 자주 허용하는 관리자에게 유용
    • 날짜, 시간 또는 특별 이벤트에 따라 사용자 수의 변동 폭이 크면 액세스를 반복해서 허용 또는 취소해야 할 경우 유용
  • SAML (security assertion markup language token) 보다 간결하고 데이터 크기도 훨씬작다.
  • 확장성이 좋다.
  • 데이터 위조 변조를 방지

사용시 주의사항

  1. alg:none ( header ) : 가끔 어떤 사이트들은 alg 가 none 일 경우 인가해주는 경우 존재
  2. Decoding : JWT 디코딩은 쉬운 편이다. 그래서 민감한 정보를 많이 넣느 것을 주의하고 최소한의 정보만 넣어야 한다.
  3. 시크릿키 문제: 개발자가 시크릿 키를 대충 구현하면 공격받기 쉽다.
    1. 공유금지, 길게 설정하기
  4. 탈취에 유의 
    1. 블랙리스트 생성, 유효기간 짧게 설정, 훔치기 힘든 저장소( http only cookie)에 JWT 저장

 

 

 

 

참고자료

JWT공식문서

네이버 cloud platform

wikipeida

IBM

github: node-jsonwebtoken

okta

https://velog.io/@j_user0719/JWT-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%A3%BC%EC%9D%98%EC%82%AC%ED%95%AD

+ Recent posts