1. 나름 안정적인 운영
개발을 하다보면, 익숙한 환경을 벗어나 새로운 환경을 시도해보고 싶을 때가 있습니다.
백엔드 개발자가 갑자기 프론트엔드를 한다던가... 앱 개발을 한다던가....
국비지원 자바로 시작해서 스프링으로 넘어가고,
로아투두도 그렇고 현재는 주로 스프링 부트를 사용해 백엔드 개발을 해오고 있습니다.
최근에 느낀점은
새로운 프로젝트를 시작해도, 이미 만들어진 틀이 있어
spring starter에서 Security, JPA, QueryDSL 등 필요한 라이브러리 가져오고
대충 config 파일 만들어서 세팅해주고
도커 파일 만들고 AWS에서 설정 해주면 CI/CD 들어가서 배포까지 깔끔하게 됩니다.
이렇게 하면 t4g.micro EC2를 예약 인스턴스를 구매했다는 가정하에
하루에 2달러 내외의 서버비가 나가게 되고
한달 Request 150만건, 50 기가의 데이터 통신일 때,
메모리도 어느정도 안정적으로 유지할 수 있습니다.
2. 좀 더 서버비를 적게 할 수 없을까?
하루에 2달러 내외... 어떻게 보면 적을수도, 어떻게 보면 많을수도 있는 금액이지만
여기서 서버비를 줄이고, ElasticSearch 서버를 구성하고 싶어 서버비를 줄일 수 있는 방법을 찾아보게 됩니다.
그러다 발견한게 서버리스, AWS Lambda 입니다.
한달 100만건까지는 AWS에서 무료로 사용가능하고, 사이트 특성상 API 자체의 메모리 사용량은 크지않기 때문에,
비용이 지금보다 훨씬 줄어들 것이라는 판단이 들었습니다.
3. 어떤 기술을 쓰지?
Lambda는 거의 대부분의 언어를 사용할 수 있기 때문에 자바, 파이썬, 자바스크립트 모두 가능했습니다.
Spring도 Spring Cloud를 사용하면 AWS Lambda로 배포할 수 있지만, JVM 환경이기 때문에 기본적인 메모리 사용이 있어,
다른 프레임워크를 알아보던 중 NestJS에 대해 알게되었습니다.
NodeJS 기반이라 상대적으로 메모리가 적고, 타입 스크립트 기반의 자바 스프링과 유사한 구조를 가지고 있어
로아투두 프로젝트에 알맞은 프레임워크라는 판단이 들었습니다. (또한 요즘 스타트업에서 많이 쓰고 있는 기술이라고 들었습니다.)
4. Spring VS NestJS
제가 느낀 차이점이 아래 글과 유사하여 대체합니다.
- 진짜 비슷하다....
- 자동완성은 Intelij가 더 좋지만, 코드를 수정하고 반영하는건 NestJS가 빠르다
- JPA랑 TypeORM도 비슷한데... 개인적으로 TypeORM이 더 깔끔한거 같다.
5. NestJS 기능 개발 및 배포
테스트 용도로 기존의 방명록 API와 똑같은 API를 개발하고,
AWS Lambda로 배포한 후 테스트를 해보왔습니다.
import {
Body,
Controller,
Delete,
Get,
HttpCode,
Param,
Patch,
Post,
Query,
UseGuards,
} from '@nestjs/common';
import { CommentsService } from './comments.service';
import { ResponseCommentDto } from './dto/response-comment.dto';
import { PagingDto } from 'src/paging.dto';
import {
ApiExtraModels,
ApiOperation,
ApiResponse,
ApiTags,
getSchemaPath,
} from '@nestjs/swagger';
import { CreateCommentDto } from './dto/create-comment.dto';
import { GetMember } from 'src/domain/member/get-member.decorator';
import { MemberEntity } from 'src/domain/member/entities/member.entity';
import { AuthGuard } from '@nestjs/passport';
import { UpdateCommentDto } from './dto/update-comment.dto';
@Controller('comments')
@ApiTags('comments')
export class CommentsController {
constructor(private readonly commentsService: CommentsService) {}
@Get()
@ApiOperation({ summary: '방명록 데이터 불러오기(페이징)' })
@ApiExtraModels(PagingDto, ResponseCommentDto)
@ApiResponse({
status: 200,
schema: {
allOf: [
{ $ref: getSchemaPath(PagingDto) },
{
properties: {
data: {
type: 'array',
items: { $ref: getSchemaPath(ResponseCommentDto) },
},
},
},
],
},
})
findAll(
@Query('page') page: number = 1,
): Promise<PagingDto<ResponseCommentDto[]>> {
return this.commentsService.findAll(page);
}
@Post()
@HttpCode(200)
@ApiOperation({ summary: '방명록 저장' })
@UseGuards(AuthGuard('jwt'))
create(
@GetMember() member: MemberEntity,
@Body() createCommentDto: CreateCommentDto,
): Promise<void> {
return this.commentsService.create(createCommentDto, member);
}
@Patch()
@ApiOperation({ summary: '방명록 수정' })
@UseGuards(AuthGuard('jwt'))
update(
@GetMember() member: MemberEntity,
@Body() updateCommentDto: UpdateCommentDto,
): Promise<void> {
return this.commentsService.update(updateCommentDto, member);
}
@Delete('/:id')
@ApiOperation({ summary: '방명록 삭제' })
@UseGuards(AuthGuard('jwt'))
delete(
@GetMember() member: MemberEntity,
@Param('id') id: number,
): Promise<void> {
return this.commentsService.delete(id, member);
}
}
정상적으로 작동이 되고, 속도 또한 Spring으로 배포한 것과 유사한 속도를 보였습니다.
한가지 걸리는 것이 있다면 Lambda의 Cold Start 인데... 기능이 전부 Lambda로 옮겨진다면 괜찮을 것 같다고 판단하였습니다.
6. 하지만....
Lambda가 물론 좋다고 생각하지만 한가지 문제가 있었습니다.
옮길 API가 너무 많다...는 것 입니다....
방명록 이후 캐릭터 관련 API 이전 작업을 하면서
캐릭터 테이블 스키마 옮기고... 숙제 테이블, 콘텐츠 테이블, 거래소 테이블, 등등 옮기고 테스트 해야하는 테이블이 늘어나면서...
시간이 꽤 든다는 생각이 들었습니다.
짧게 몰아서 할만한 양은 아닌거 같고...
스프링쪽에서 기능 개발을 하면서 동시에 이전 작업을 같이해야 할 것 같습니다.
언제가 될지 모르겠지만...