10. 웹 API서버 만들기

진영민·2022년 9월 6일
0

Node.js 교과서

목록 보기
8/13

해당 글은 Node.js 교과서의 내용을 요약, 정리한 글입니다.

API 서버 이해하기

API

Application Programming Interface의 약자
다른 애플리케이션에서 현재 프로그램의 기능을 사용할 수 있게 허용하는 접점을 의미한다.

크롤링

크롤링은 웹 사이트가 자체적으로 제공하는 API가 없거나 API이용에 제한이 있을 때 사용하는 방법이다.
표면적으로 보이는 웹 사이트의 정보를 일정 주기로 수집해 자체적으로 가공하는 기술이다.

하지만 웹 사이트에서 직접 제공하는 API가 아니므로 원하는 정보를 얻지 못할 가능성이 있다. 또한, 웹 사이트에서 제공하길 원치 않는 정보를 수집한다면 법적인 문제가 발생할 수도 있습니다.

제공자 입장에서도 주기적으로 크롤링을 당하면 웹 서버의 트래픽이 증가하여 서버에 무리가 가므로, 웹 서비스를 만들 때 공개해도 되는 정보들은 API로 만들어 API를 통해 가져가게 하는 것이 좋다.

프로젝트 구조 갖추기

이번 프로젝트는 NodeBird 서비스와 데이터베이스를 공유한다.
다른 서비스에 NodeBird 서비스의 게시글, 해시태크, 사용자 정보를 JSON 형식으로 제공하되, 인증을 받은 사용자에게만 일정한 할당량 안에서 API를 호출할 수 있도록 허용할 것이다.

동일하게 npm init으로 시작한다.
패키지들을 설치하고, 에러를 표시할 파일을 만든다.

포트 번호를 다르게 설정하면 여러 서버를 동시에 열 수 있다.

JWT 토큰으로 인증하기

JWT 토큰

JSON Web Token의 약자

  • 헤더: 토큰 종류와 해시 알고리즘 정보가 들어 있다.
  • 페이로드: 토큰의 내용물이 인코딩된 부분이다.
  • 시그니처: 일련의 문자열이며, 시그니처를 통해 토큰이 변조되었는지 여부를 확인할 수 있다.

시그니처는 JWT 비밀 키로 만들어진다. 이 비밀 키가 노출되면 JWT 토큰을 위조할 수 있으므로 비밀 키를 철저히 숨겨야 한다.

JWT 토큰의 장점

  • JWT 토큰은 JWT 비밀 키를 알지 않는 이상 변조가 불가능하다.
  • 심지어 변조한 토큰은 시그니처를 비밀 키를 통해 검사할 때 들통난다. 변조가 불가능하므로 내용물의 변질에 대해 걱정할 필요가 없다.
  • 사용자 인증 권한을 최소화시킨다.
  • 비밀번호를 제외하고 사용자의 이메일이나 사용자의 권한 같은 것을 넣어두면 데이터베이스 조외없이도 그 사용자를 믿고 권한을 줄 수 있다.

JWT 토큰의 단점

  • 용량이 크다.
  • 내용물이 들어있으므로 랜덤한 토큰을 사용할 때와 비교하면 용량이 클 수밖에 없다.
  • 매 요청마다 데이터양이 증가한다.
  • 매 요청시 내용물이 들어있는 토큰들이 오고가므로 데이터양이 증가할 수밖에 없다.
$npm i jsonwebtoken

해당 명령어를 이용해 JWT 모듈을 설치한다.

다른 사용자가 API를 쓰려면 JWT 토큰을 발급받고 인증받아야 한다. 이는 대부분의 라우터에 공통적으로 해당하는 부분이므로 미들웨어로 만들어두는 게 좋다.

헤더에는 저장된 토큰을 사용한다. 사용자가 헤더에 토큰을 넣어 보낸다. jwt.verify메서드로 토큰을 검증할 수 있다.

토큰의 비밀 키가 일치하지 않는다면 인증을 받을 수 없다. 이런 경우 419 상태 코드로 응답한다.

JSON 형태는 일정한 형식을 갖춰야 응답받는 쪽에서 처리하기가 좋다.

다른 서비스에서 호출하기

API 제공 서버를 만들었으니, API를 사용하는 서비스도 만들어보자.
이는 다른 서버에 요청을 보내므로 클라이언트 역할을 한다.
또한, 주 목적은 nodebird-api의 API를 통해 데이터를 가져오는 것이다.
가져온 데이터는 JSON 형태이므로 퍼그나 넌적스 같은 템플릿 엔진으로 데이터를 렌더링 할 수 있다.

사용 제한 구현하기

인증된 사용자라도 API를 과도하게 사용하면 API 서버에 무리가 간다. 따라서 일정 기간 내에 API를 사용할 수 있는 횟수를 제한하여 서버의 트래픽을 줄이는 것이 좋다.

$npm i express-rate-limit

이제 몇 가지 설정을 해 주고 apiLimiter미들웨어를 라우터에 넣으면 라우터에 사용량 제한이 걸린다.

하지만 express-rate-limit가 추가되었으므로, 기존 API버전과 호환되지 않는다.

실제 서비스 운영 시에는 v2가 나왔다고 v1을 닫아 버리거나 하지 않는다. 일정한 시간 동안은 v1을 열어둔다.

CORS이해하기

지금까지 짰던 코드를 브라우저에서 돌려보면 CORS에러가 발생한다.
(Cross-Origin Resource Sharing)
Access-Control-Allow-Origin이라는 헤더가 없다는 내용의 에러이다.

이처럼 브라우저와 서버의 도메인이 일치하지 않으면, 기본적으로 요청이 차단된다. 이 현상은 브라우저에서 서버로 요청을 보낼 때만 발생하고, 서버에서 서버로 요청을 보낼 때는 발생하지 않는다.

먼저, 브라우저에서 서버로 Options method로 요청을 보낸다. 이후 서버에서 어떤 method가 가능한지 등의 내용을 브라우저에 보낸다. 브라우저는 이를 분석하여 서버에 보낼 요청을 확인하여 가능하는 것이 확인되면 요청을 보낸다.

서버에 보낼 요청이 불가능하다는 것이 확인되면 CORS ERROR를 날린다.
-->request doesn't pass access control check:
이 문구가 Options method로 보낸 것과 비교했을때 불가능하다는 것을 표현한다.

cors 패키지

npm에서 cors를 편하게 설치할 수 있는 패키지

$npm i cors
const cors = require('cors');

router.use(cors({
	credentials: true,
}));

이제 응답에 Access-Control-Allow-Origin헤더가 추가되어 나간다. credentials: true라는 옵션은 활성화 하면 다른 도메인 간에 쿠키가 공유된다. 서버 간의 도메인이 다른 경우 이 옵션이 활성화되지 않으면 로그인되지 않을 수 있다.

Access-Control-Allow-Origin이 *으로 설정되어 있다면 모든 클라이언트의 요청을 허용한다는 뜻이다.

먼저 도메인 모델로 클라이언트의 도메인과 호스트가 일치하는 것이 있는지 확인한다. 일치하는 것이 있다면 CORS를 허용해서 다음 미들웨어로 보내고, 일치하는 것이 없다면 CORS없이 next를 호출한다.

특정한 도메인만 허용한다면 허용되지 않은 다른 도메인에서 요청을 보내는 것을 차단할 수 있다.

profile
코린이

0개의 댓글