목차
1. API의 종류: HTTP API와 Restful API
2. Restful API의 조건
3. 프로젝트 코드 리팩토링: Restful API 설계하기
1. API의 종류: HTTP API와 REST API
API란 Application Programming Interface의 약자로 소프트웨어를 개발할 때 사용하는 통신 규칙이다. 다양한 API가 있겠지만 자주 사용되는 것이 서버와 프론트 사이에 통신할 때 주고 받는 URL 규칙이다.
API 에는 HTTP와 REST API가 있다. 이 글에 따르면 HTTP와 REST는 의미가 많이 혼동되어 사용되지만, 엄밀하게 따지면 REST API에 규칙이 조금 더 추가되어있다. 그 이유는 Rest API는 서버와 클라이언트가 서로 독립적으로 진화하기 위해서 만들어졌기 때문이다. REST API 규칙을 잘 지킨다면 서버에서 자체적으로 업그레이드를 해도 프론트에서 모든 정보를 받아올 수 있기 때문에 따로 코드를 수정할 필요가 없다. 👏👏👏
2. Restful API의 조건
REST가 되기 위한 조건으로 다음 다섯 가지를 지켜야한다.
- client-server
- stateless
- cache
- uniform interface
- layered system
- code-on-deman(optional)
HTTP 를 구현하면 나머지는 자연스럽게 구현이 되는 데 이 중에서 uniform interface의 조건을 구현하는 것이 까다로운 부분이다. 그래서 우선 이 글에서는 다른 개발자들과 개발하기에 크게 문제가 없을 조건으로 API URL을 리팩토링하고, 이후 추가적으로 엄밀한 의미의 REST API를 개발하려고 한다.
3. ABC 프로젝트의 URL을 최대한 Restful 하게 설계하기
보다 좋은 API URL을 개발하기 위해 다음 두 가지 기준으로 리팩토링 하려고 한다.
(두 가지 기준은 이 영상을 참고했습니다!)
(1) URL에서 동사형(verb)를 제거한다. 대신 http headers를 이용해 다양한 method를 지원한다.
(2) query문을 사용해 변하는 값들을 정의한다.
지금 하고 있는 프로젝트 1번의 API를 보면 다음과 같이 규칙에 맞지 않는 API가 있다.
먼저 user API를 살펴보면 다음과 같다. http 매소드를 잘 사용하고 있다.
- update라는 동사를 사용하고 있다.
- query로 사용할 수 있는 userId를 직접 URL로 작성하고 있다.
- login, logout URL에는 users를 쓰지 않아도 괜찮다.
이 프로젝트는 NestJS를 사용하고 있기 때문에 URL을 정의하는 users.controller를 수정한다.
하나의 예로 유저 정보 수정의 코드를 보면 다음과 같이 @Patch 뒤에 '/update/:userId'로 update라는 http 매소드를 포함하고 있다. 따라서 이 부분을 제거한다.
// * 유저 정보 수정
@ApiResponse({
status: 500,
description: '회원 정보 수정 Server Error...',
})
@ApiResponse({
status: 200,
description: '회원 정보 수정 성공',
type: ReadOnlyUserDto,
})
@ApiOperation({ summary: '회원 정보 수정' })
@Patch('/update/:userId')
async updateUser(@Param('userId') id, @Body() body) {
return await this.usersService.updateUser(id, body);
}
수정 후:
- URL에 update라는 동사를 없앴다. NestJS의 특성 상 이 모듈의 이름이 붙으므로 users 는 기본적으로 사용된다
- 회원 정보에 대한 정보를 body로 userId를 Query로 전달받는다.
// * 유저 정보 수정
@ApiResponse({
status: 200,
description: '회원 정보 수정 성공',
type: ReadOnlyUserDto,
})
@ApiResponse({
status: 500,
description: '회원 정보 수정 Server Error...',
})
@ApiOperation({ summary: '회원 정보 수정' })
@Patch('/')
async updateUser(@Query('userId') userId, @Body() body) {
return await this.usersService.updateUser(userId, body);
}
이에 대한 테스트 코드를 추가한다.
users.controller.spec.ts에서 updateUser 함수에 대한 테스트 코드를 작성한다.
controller.updateUser가 service의 updateUser 함수를 불러와 사용하므로 다음과 같이 테스트를 짰다.
- toHaveBeenCalled: controller updateUser를 부르면 service의 updateUser를 부른다.
- 단 한 번만 부른다.
- id, 와 body parameter를 사용한다.
- 함수를 사용한 결과와 result 결과가 일치한다.
it('회원 정보를 성공적으로 수정한다', async () => {
let result ;
jest.spyOn(service, 'updateUser').mockImplementation(async () => result);
const id = {
userId: '615922e075d9da472c64491a',
};
const body = {
nickname: 'garry',
passwd: '1234',
};
await controller.updateUser(id, body);
expect(service.updateUser).toHaveBeenCalled()
expect(service.updateUser).toHaveBeenCalledTimes(1);
expect(service.updateUser).toHaveBeenCalledWith(id, body);
expect(await controller.updateUser(id, body)).toBe(result);
});
https://www.youtube.com/watch?v=4DxHX95Lq2U
https://www.youtube.com/watch?v=RP_f5dMoHFc
'네트워크' 카테고리의 다른 글
#020. Web 요청 과 응답과정 (0) | 2021.10.22 |
---|---|
#018. TCP 와 UDP (0) | 2021.10.22 |
#009 웹서버 vs WAS (0) | 2021.10.22 |