반응형

nest.js

 

이번 시간에는 TypeORM 모듈을 이용해서 DB을 연결해서 간단한 게시판(Board 모듈) CRUD를 진행해 보도록 하겠습니다.

 

준비사항 : mysql DB

간단하게 docker를 이용해서 local에서 mysql 사용하고 싶으시다면 참고 하시면 좋을 것 같습니다.

docker-compose.yml

version: '3.1'

services:
  db:
    image: mysql
    restart: always
    command: --lower_case_table_names=1 # 대소문자 구분
    container_name: mysql-db
    environment:
      - MYSQL_DATABASE=test
      - MYSQL_ROOT_PASSWORD=1234
      - TZ=Asia/Seoul
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
    volumes:
      - ./db:/var/lib/mysql # db 볼륨 처리
    ports:
      - 3306:3306

      # docker-compose up -d

 

1. TypeORM 모듈 install

  - 저는 mysql db를 사용하도록 하겠습니다.

  - mysql과 typeorm 모듈을 설치합니다.

npm install mysql2 typeorm @nestjs/typeorm

 

2. TypeORM config 설정

config.typeorm.ts

import { TypeOrmModuleOptions } from "@nestjs/typeorm";

export const typeORMConfig : TypeOrmModuleOptions = {
    type: 'mysql',
    host: 'localhost',
    port: 3306,
    username: 'root',
    password: '1234',
    database: 'test',
    entities: [__dirname+ '/../**/*.entity.{js,ts}'],
    synchronize:true
}

 

app.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { typeORMConfig } from './configs/config.typeorm';

@Module({
  imports: [
    TypeOrmModule.forRoot(typeORMConfig)
  ],
})
export class AppModule {}

 

3. board 모듈을 만들어 보도록 하겠습니다.

board 모듈 구조는 아래 그림과 같습니다.

board 모듈 구조

board.entity.ts

  - DB 테이블이 되는 Entity 객체

import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm";
import { BoardStatus } from "./board-status.enum";

@Entity()
export class Board extends BaseEntity {

    @PrimaryGeneratedColumn()
    id:number;

    @Column()
    title:string;

    @Column()
    description:string;

    @Column()
    status:BoardStatus;
}

 

board.repository.ts

import { Repository } from "typeorm";
import { Board } from "./board.entity";

export class BoardRepository extends Repository<Board> {
}

 

boards.controller.ts

import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UsePipes, ValidationPipe } from '@nestjs/common';
import { BoardsService } from './boards.service';
import { BoardStatus } from './board-status.enum';
import { CreateBoardDto } from './dto/create-board.dto';
import { Board } from './board.entity';

@Controller('boards')
export class BoardsController {
    constructor( private boardsService : BoardsService) {} 
    // nest에서는 boardsService 인스턴스를 프로퍼티라고 부르는 것 같다

    @Get('/:id')
    getBoardById(@Param('id') id:number) : Promise<Board> {
        return this.boardsService.getBoardById(id);
    }

    @Post()
    @UsePipes(ValidationPipe)
    createBoard(@Body() createBoardDto: CreateBoardDto) : Promise<Board> {
        return this.boardsService.createBoard(createBoardDto);
    }

    @Patch('/:id/status')
    updateBoardStatus(
        @Param('id', ParseIntPipe) id: number,
        @Body('status') status : BoardStatus
    ) {
        return this.boardsService.updateBoardStatus(id, status);
    }

    @Delete('/:id')
    deleteBoard(@Param('id', ParseIntPipe) id: number) {
        return this.boardsService.deleteBoard(id);
    }

}

  - validationPipe를 사용하기 위해서는 아래와 같이 npm install이 필요합니다.

설치하게되면 기본적인 validation 체크를 위한 데코레이터를 사용할 수 있습니다.

npm install class-validator class-transformer --save

 

boards.service.ts

import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BoardStatus } from './board-status.enum';
import { Board } from './board.entity';
import { CreateBoardDto } from './dto/create-board.dto';

@Injectable() // nest js 어디에서나 사용할 수 있게 해준다
export class BoardsService {

    constructor(
        @InjectRepository(Board)
        private boardRepository : Repository<Board>,
    ){}

    async getBoardById(id: number): Promise <Board> {
        const found = await this.boardRepository.findOneBy({id});
        if(!found) {
            throw new NotFoundException(`Can't find Board with id ${id}`);
        }
        return found;
    }

    async createBoard(createBoardDto : CreateBoardDto) : Promise<Board> {
        const {title, description} = createBoardDto;

        const board = this.boardRepository.create({
            title,
            description,
            status: BoardStatus.PUBLIC
        })

        await this.boardRepository.save(board);
        return board;
    }

    async updateBoardStatus(id:number, status: BoardStatus) : Promise<Board> {
        const board = await this.getBoardById(id);
        
            board.status = status;
            await this.boardRepository.save(board);

            return board;
    }

    async deleteBoard(id:number) : Promise<void> {
        const result = await this.boardRepository.delete(id);

        if(result.affected === 0 ) {
            throw new NotFoundException(`Can't find Board with id ${id}`);
        }
    }

}

 

create-board.dto.ts

  - dto class

import { IsNotEmpty } from "class-validator";

export class CreateBoardDto{
    @IsNotEmpty()
    title:string;

    @IsNotEmpty()
    description:string;
}

 

board-status.enum.ts

  - BoardStatus를 상수로 쓰기 위한 enum

// 게시판 모델 정의
// interface : 변수의 타입만의 체크한다.
// classes   : 변수의 타입도 체크하고 인스턴스 생성할 수 있다.

// export interface Board {

//     id: string;
//     title: string;
//     description: string;
//     status: BoardStatus

// }

export enum BoardStatus {
    PUBLIC = 'PUBLIC',
    PRIVATE = 'PRIVATE'
}

 

boards.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Board } from './board.entity';
import { BoardsController } from './boards.controller';
import { BoardsService } from './boards.service';

@Module({
    imports: [
        TypeOrmModule.forFeature([Board])
    ],
    controllers: [BoardsController],
    providers: [BoardsService]
})
export class BoardsModule {}

 

app.module.ts

  - imports에 BoardsModule을 추가합니다.

import { Module } from '@nestjs/common';
import { BoardsModule } from './boards/boards.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { typeORMConfig } from './configs/config.typeorm';

@Module({
  imports: [
    TypeOrmModule.forRoot(typeORMConfig),
    BoardsModule
  ],
})
export class AppModule {}

 

참조

- 따라하며 배우는 NestJS

728x90
반응형

'NestJS' 카테고리의 다른 글

Jest에서 의존성 주입과 Mock 데이터를 설정하는 방법  (0) 2025.02.04
[Nest.js] PIPE  (0) 2023.08.27
[Nest.js] 기본 파일 구조 및 요청 흐름  (2) 2023.08.20
[Nest.js] Quick Start!  (0) 2023.08.15
반응형

nest.js

 

지난 시간에 nest.js 셋업에 이어서

이번 시간에는 nest.js client로 프로젝트를 생성하였을 때 기본적으로 생성되는 파일client 요청에 따른 흐름을 알아보겠습니다.

 

1. 기본적으로 생성되는 파일

 

 

1.eslintrc.js : 개발자들이 특정한 규칙을 가지고 코드를 깔끔하게 짤 수 있도록 도와주는 라이브러리, 타입스크립트를 쓰는 가이드 라인 제시, 문법에 오류가 나면 알려주는 역할 등

2. prettierrc : 주로 코드 형식을 맞추는데 사용합니다. 작은 따옴표를 사용할지 큰 따옴표를 사용할지 Indent 값을 두줄로 줄지 네줄로 줄지등등 에러 찾는 것이 아닌 코드 포멧터 역할

3. nest-cli.json : nest 프로젝트를 위한 특정한 설정을 할 수 있는 json 파일

4. tsconfig.json : 어떻게 타입스크립트를 컴파일 할지 설정

5. tsconfig.build.json : tsconfig.json의 연장선상 파일이며, build를 할 때 필요한 설정등 "excludes"에서는 빌드할 때 필요 없는 파일들 명시

6. package.json : build : 운영환경을 위한 빌드, format: 린트에러가 났을지 수정, start : 앱 시작 

7. src : 대부분의 비지니스 로직이 들어가는 곳

 

 

2. client 요청에 따른 흐름

client(브라우저)에서 localhost:3000번으로 요청을 보내면

nest.js 애플리케이션에서는 app.module 요청에 대한 진입점 역할을 하는데 요청을 구분해서 등록되어 있는 controller에 요청을 전달합니다.

controller로 요청을 보내고, service에서 'Hello World!'라는 문자열을 리턴 받아서 client에서 응답하는 흐름입니다.

 

1. app.module.ts

  - controllers에 AppController가 providers에 AppService가 등록되어 있습니다.

  - module에 대해서는 아래에서 더 상세하게 설명을 하도록 하겠습니다.

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

 

2. app.controller.ts

  - @Get 데코레이터를 보시면 "/" 요청이 오면 getHello() 함수를 실행하도록 되어 있습니다.

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

 

3. app.service.ts

import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

 

모듈이란

모듈은 @Module() 데코레이터로 주석이 달린 클래스입니다. @Module() 데코레이터는 Nest가 애플리케이션 구조를 구성하는 데 사용하는 메타 데이터를 제공합니다. 

각 응용 프로그램에는 하나 이상의 모듈이 있습니다. 루트 모듈(AppModule)은 Nest가 사용하는 시작점입니다.

모듈은 밀접하게 관련있는 기능 집합으로 구성 요소를 구성한느 효과적인 방법입니다.

모듈은 기본적으로 싱글 톤이므로 여러 모듈간의 쉽게 공급자의 동일한 인스턴스를 공유 할 수 있습니다.

 

참조 

- 따라하며 배우는 NestJS

728x90
반응형

'NestJS' 카테고리의 다른 글

Jest에서 의존성 주입과 Mock 데이터를 설정하는 방법  (0) 2025.02.04
[Nest.js] PIPE  (0) 2023.08.27
[Nest.js] TypeORM 이용한 CRUD  (0) 2023.08.22
[Nest.js] Quick Start!  (0) 2023.08.15
반응형

nest.js

 

 

1. Nest.js 사용이유

Nest.js는 구조화된 코드 작성, 의존성 관리, 유연한 미들웨어 처리, 데이터 유효성 검사등의 장점을 제공하여 백엔드 애플리케이션 개발을 효율적이고 유지보수하기 쉽게 만들어주는 프레임워크입니다.

 

2. Nest.js 특징

1. 모듈러 아키텍처 : Nest.js는 애플리케이션을 모듈 단위로 구성하여 코드를 구조화하고 유지보수하기 쉽게 만들어줍니다.

2. 의존성 주입 : 의존성 주입은 컴포넌트 간의 의존성을 효율적으로 관리하고 분리하는 방법을 제공합니다.

3. 데코레이터 : 데코레이터는 컨트롤러, 서비스 등의 메타데이터를 정의하여 사용합니다. 이를 통해 라이팅, 유효성 검사 등을 더 쉽게 처리할 수 있습니다.

 

3. Express 프레임워크와 비교

Node.js를 이용한 백엔드 서버는 Express가 많이 보편적으로 사용되어 왔습니다. 미니멀리스트적인 접근 방식을 취하고, 기본적인 웹 프레임워크로서 필요한 기능만 제공하며, 개발자가 직접 확장하거나 추가 기능을 구현해야 합니다.

반면에 Nest.js는 구조화된 코드, 의존서 주입, TypeScript의 강력한 지원과 같은 장점을 통해 큰 규모의 백엔드 애플리케이션을 더 효율적으로 개발하고 유지보수 할 수 있도록 도와줍니다.

 

4. Nest.js Setup

Nest CLI를 사용하여 새 프로젝트를 설정할 수 있습니다.

npm이 설치된 상태에서 터미널에 아래와 같이 명령어를 사용하여 Nest 프로젝트를 만들어줍니다.

 

4.1) 프로젝트 생성

$ npm i -g @nestjs/cli
$ nest new project-name

 

디렉토리가 프로젝트 이름으로 생성되고 노드 모듈 및 여러 파일과 src/ 생성되어 여러 코어 파일이 생성됩니다.

 

main.ts

 - NestFactory 핵심 기능을 사용하여 Nest 애플리케이션 인스턴스를 생성하는 애플리케이션 엔트리 파일입니다.

 - app.listen() : 웹 어플리케이션 포트

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

 

4.2) 프로젝트 실행

$ npm run start			프로젝트 실행
$ npm run start:dev		파일의 변경 사항을 적용하려면 개발 모드로 실행

 

http://localhost:3000 으로 접속 성공

다음장에서는 기본적으로 생성된 파일 역할을 간략하게 설명하고 localhost:3000에 접속하였을 때 nest.js 애플리케이션의 간단한 진행 흐름을 알아보도록 하겠습니다.

 

 

 

참고 

- https://docs.nestjs.com/first-steps

 

728x90
반응형
반응형

개요 : 아주 간단하게 Docker를 이용해서 Spring 프로젝트 이미지를 생성 후 실행해 보도록 하겠습니다.

 

1. spring project build

  - WebController.java

@Controller
public class WebController {

    @GetMapping("/")
    public String sample() {
        return "sample";
    }

}

  - sample.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Docker</title>
</head>
<body>
  <span>Docker!</span>
</body>
</html>

 

- gradle 기준 : 터미널에서 ./gradlew build

 

2. Dockrfile 생성

  - 프로젝트 Root 경로 바로 아래에 Dockerfile 생성

FROM openjdk:11  # 컴파일 할 jdk 버전

WORKDIR /usr/src/app # 모든 작업 파일 기준 위치

ARG JAR_PATH=./build/libs # 변수 사용하듯이 선언해서 사용

COPY ${JAR_PATH}/IPGeoCheck-0.0.1-SNAPSHOT.jar ${JAR_PATH}/IPGeoCheck-0.0.1-SNAPSHOT.jar
# 빌드한 jar 파일을 도커 컨터이너 내부로 옮겨주는 작업

CMD ["java","-jar","./build/libs/IPGeoCheck-0.0.1-SNAPSHOT.jar"]
# jar 파일 실행 명령

 

3.  도커 이미지 빌드

  - 터미널에서 아래와 같이 명령어 실행(도커 파일이 있는 위치에서)

docker build . -t springbootapp (도커 이미지 빌드)

 

 

4. 도커 이미지 실행

  - p 옵션을 넣어서 실행한다.

    도커 내부 네트워크와 외부 네트워크를 연결하기 위한 포트 연결(로컬 포트 / 도커 내부 포트)

    주의 : 포트 옵션을 주지 않으면 도커 이미지를 실행 되었으나 접근이 안되는 현상이 발생된다.

docker run -p 8080:8080 springbootapp(이미지 실행)

※ 문제점 : 어플리케이션 코드의 변경으로 인해서 도커 이미지를 매번 다시 빌드해서 실행해야 한다.

이러한 불편함을 해소하고자 Volume 옵션을 사용한다.

 

 

5. 볼륨(Volume) 옵션을 이용한 도커 이미지 실행

docker run -p 8080:8080 -v $(pwd):/usr/src/app springbootapp(볼륨을 사용한 이미지 실행)
# -v "local 참조할 경로" : "참조할 도커 이미지 경로"
Volume 옵션은 실행에 필요한 파일을들 컨테이너 내부에서 참조할 수 있도록 해줍니다.
로컬 경로에 존재하는 모든 파일들을 도커 컨테이너 내부에서 사용할 수 있습니다.

 

728x90
반응형

'Docker' 카테고리의 다른 글

[Docker] docker compose 이용해서 Wordpress 세팅  (0) 2023.09.26
반응형

자바의 자료형은 크게 기본 타입(primitive type)과 참조 타입(reference type)으로 나뉩니다.

기본 타입은 byte, char, short, int, long, float, double, boolean

참조 타입은 class, interface가 있습니다.

이때 기본 자료타입(primitive type)을 객체로 다루기 위해서 사용하는 클래스들을 래퍼 클래스(wrapper class)라고 합니다.

 

래퍼 클래스는 기본 유형을 래핑하고 변환 메서드, 비교 메서드 및 유틸리티 메서드와 같은 추가 기능을 제공하는 Java 표준 라이브러리의 클래스입니다. 각 기본 유형에는 해당 래퍼 클래스가 있습니다.

 

  • Byte: wraps a byte value
  • Short: wraps a short value
  • Integer: wraps an int value
  • Long: wraps a long value
  • Float: wraps a float value
  • Double: wraps a double value
  • Character: wraps a char value
  • Boolean: wraps a boolean value

 

래퍼 클래스는 기본 유형을 객체로 취급해야 하는 상황에서 주로 사용됩니다. 예를 들어 Integer 클래스를 사용하여 컬렉션에 int 값을 저장하거나 Integer 개체를 개체를 예상하는 메서드에 대한 인수로 전달할 수 있습니다.

요약하면 기본 유형과 래퍼 클래스의 주요 차이점은 기본 유형은 프로그래밍 언어에 내장된 기본 데이터 유형인 반면 래퍼 클래스는 기본 유형을 래핑하고 추가 기능을 제공하는 클래스라는 것입니다.

 

int Integer
기본타입 참조타입
산술 연산 가능함 산술 연산 불가능
null로 초기화 불가능 null 초기화 가능

 

추가적으로 기본 타입의 데이터를 wrapper 클래스의 인스턴스로 변환하는 과정을 박싱(Boxing)이라고 하고, 래퍼 클래스의 인스턴스에 저장된 값을 다시 기본 타입의 데이터로 꺼내는 과정을 언박싱(UnBoxing)이라고 합니다.

728x90
반응형

'JAVA' 카테고리의 다른 글

Java 스트림(Stream) 정리  (0) 2021.07.17
반응형

오류 처리는 더 견고한 서비스를 위해서 개발자에게 매우 중요합니다.

이번 시간에는 SpringBoot에서는 기본적으로 어떻게 오류 페이지를 처리하고 있는지, 어떻게 커스텀 하게 수정해서 오류 페이지를 관리하는지 알아보도록 하겠습니다.

 

1. SpringBoot에서는 기본적으로 BasicErrorController에서 오류를 관리하고 있습니다.

BasicErrorController.java

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {

	private final ErrorProperties errorProperties;

	/**
	 * Create a new {@link BasicErrorController} instance.
	 * @param errorAttributes the error attributes
	 * @param errorProperties configuration properties
	 */
	public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
		this(errorAttributes, errorProperties, Collections.emptyList());
	}

	/**
	 * Create a new {@link BasicErrorController} instance.
	 * @param errorAttributes the error attributes
	 * @param errorProperties configuration properties
	 * @param errorViewResolvers error view resolvers
	 */
	public ₩(ErrorAttributes errorAttributes, ErrorProperties errorProperties,
			List<ErrorViewResolver> errorViewResolvers) {
		super(errorAttributes, errorViewResolvers);
		Assert.notNull(errorProperties, "ErrorProperties must not be null");
		this.errorProperties = errorProperties;
	}

	@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
	public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
		HttpStatus status = getStatus(request);
		Map<String, Object> model = Collections
				.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
		response.setStatus(status.value());
		ModelAndView modelAndView = resolveErrorView(request, response, status, model);
		return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
	}

	@RequestMapping
	public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
		HttpStatus status = getStatus(request);
		if (status == HttpStatus.NO_CONTENT) {
			return new ResponseEntity<>(status);
		}
		Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
		return new ResponseEntity<>(body, status);
	}
   }

 - @RequestMapping("${server.error.path:${error.path:/error}}")

    -> 설정 파일에 error.path 값이 없으면 default로 /error를 사용합니다.

 - HTML로 응답을 주는 경우 errorHtml 메소드로 응답을 합니다.

 - HTML 외 응답은 error 메소드에서 처리합니다.

 

2. 에러 관련 설정 파일

application.yml

server:
  error:
    whitelabel:
      enabled: true           # 화이트 라벨 페이지 유무 (default : true) 
    include-stacktrace: never # 오류 응답에 stacktrace 내용을 포함할 지 여부 (default : always)
    path: /error              # 오류 응답을 처리할 핸들러(ErrorController) path (default : /error)

 

3. 에러 발생하면 에러 페이지 HTML을 노출시키도록 해보겠습니다.

개발 환경

  •  Spring 2.7.5
  •  Thymeleaf

 

기본적으로 오류 페이지를 노출시키는 방법은 간단합니다.

저는 Thymleaf를 사용하고 있기 때문에 resources > templates > error 디렉토리 아래에 에러 코드 이름으로

html 파일을 생성해주면 됩니다.

 * 400번대 에러코드를 모두 커버하고 싶다면 4xx.html로 생성하면 되겠습니다.

 

404.html

참 쉽죠? 이제 직접 ErrorController를 구현해서 에러 페이지에 데이터를 전달해 보도록 하겠습니다.

4. @ErrorController

WebErrorController.java

@Controller
public class WebErrorController implements ErrorController {

    @RequestMapping(value = "/error")
    public ModelAndView handleNoHandlerFoundException(HttpServletResponse response, HttpServletRequest request, Model model) {
        Object statusCode = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
        // int status = response.getStatus();

        ModelAndView modelAndView = new ModelAndView();

        if (statusCode != null) {
            Integer status = Integer.valueOf(statusCode.toString());
            if (status == HttpStatus.NOT_FOUND.value()) {
                modelAndView.addObject("errorCode", status);
                modelAndView.setViewName("/error/404");
            } else if (status == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
                modelAndView.addObject("errorCode", status);
                modelAndView.setViewName("/error/500");
            } else if (status == HttpStatus.FORBIDDEN.value()) {
                modelAndView.setViewName("/error/403");
            } else modelAndView.setViewName("/error/common");
        }

        return modelAndView;
    }
}

 - ErrorController를 구현합니다.

 - request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE), response.getStatus()으로 에러 코드를 꺼냅니다.

 

 

404.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h3>어이쿠 오류다!</h3>
    <h2 th:text="${errorCode}"></h2>
</body>
</html>

404.html

추가적으로

@ExceptionHandler를 사용해서 해당 애노테이션이 선언된 예외 및 하위 예외에 대해서 특정 메서드가 처리할 수 있도록 해줍니다.

상황에 맞추어서 리턴 형식을 반환할 수 있습니다.

    @ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
    @ExceptionHandler(Exception.class)
    public Map<String, String> handle(Exception e) {
        Map<String, String> errorAttributes = new HashMap<>();
        errorAttributes.put("code", "NOT_IMPLEMENTED");
        errorAttributes.put("message", e.getMessage());
        return errorAttributes;
    }
728x90
반응형
반응형

AWS에서 제공하는 인증서 관리 서비스인 ACM(AWS Certificate Manager)을 이용해서 웹 서버에 SSL을 적용해 보도록 하겠습니다.

 ACM 공식 문서를 한 번 읽어 보시면 좋을 것 같습니다.

  • 이 방법은 AWS에서 제공하는 인증서 관리 서비스로 갱신에 대한 신경을 쓸 필요가 없습니다.
  • 간편하게 사용이 가능하고 유효기간은 13개월이며, ACM에서 인증서 발급은 무료입니다.

사전 준비 상태

  • 도메인 구매 완료
  • EC2 Instances
  • AWS Rout 53 사용

SSL 적용 순서

  1. ACM을 이용한 인증서 발급
  2. 로드밸런서 생성
  3. Route 53 등록

1.  ACM을 이용한 인증서 발급

 

1.1 퍼블릭 인증서 요청 선택합니다.

1.2 완전히 정규화된 도메인 이름에 사전에 발급 받으신 도메인을 작성합니다.

 - 검증 방법은 Route 53을 이용하기 때문에 DNS 검증을 선택하면 보다 더 편리하게 자동으로 갱신됩니다.

 - 키 알고리즘은 가장 널리 사용되는 RSA 2048을 사용하겠습니다.

1.3 인증서 검증을 위해서 발급된 인증서에 들어가셔서 "Rout 53에서 레코드 생성"을 선택합니다.

 - Route 53에 들어가 보시면 레코드 리스트에 CNAME 유형으로 생성된 걸 확인할 수 있습니다.

 

2.  로드밸런서 생성

현재 발급받은 인증서는 사용 중이 아니라서 갱신 자격이 부적격입니다.(로드밸런서에 연결하면 적격으로 변경됩니다.)

ACM 인증서는 Elastic Load Balancing(ELB)과 같은 다른 AWS 서비스와 연결되어야 합니다.

로드밸런서를 생성하도록 하겠습니다.

 

2.1 Application Loa Balancer로 생성하도록 하겠습니다.

 

2.2 Basic configuration

  - Load balancer name을 작성해 주시고 다른 설정은 그대로 두셔도 무방합니다.

2.3 Network mapping

  - Mappings는 두개 이상 선택해줍니다.

2.4 Security groups

  - 보안 그룹은 EC2 Instances에 등록된 보안 그룹을 추가해 줍니다.

  - 80, 443 번 포트가 오픈되어 있어야 합니다.

2.5 Listeners and routing에 인증서를 연결할 EC2 Instances를 연결합니다.

- 연결할 인스턴스가 안 보일 경우 create target group을 선택해서 생성합니다.

2.6 위 설정을 모두 하셨다면 로드밸런서를 생성합니다.

 - 생성된 로드밸런서에 리스너 탭으로 이동하면 위에서 등록한 리스너가 보입니다.

 - 443 리스너에 인증서 보기/편집을 선택해서 앞에서 생성한 인증서를 연결합니다.

2.7 80 포트는 443 포트로 리디렉션를 해주도록 하겠습니다.

 - 규칙 보기/편집을 선택합니다.

 

 

3.  Rout 53 등록

3.1 Rout 53에 위에서 생성한 로드밸런서를 레코드에 생성합니다.

3.2 SSL 연결이 끝났습니다. 

 - 브라우저에서 해당 도메인에 접근하시면 자물쇠 모양과 발급된 인증서를 확인할 수 있습니다.

 

주의사항

1. 로드밸런서 대상 그룹에 해당 인스턴스 Health status가 정상인지 확인합니다.

- Health checks 탭에서 Path 등을 변경해서 인스턴스 상태를 체크할 수 있습니다.

- Zone이 위에서 선택한 Network mapping과 같은지 확인합니다.

 

2. Route 53에 서브도메인을 www로 설정해서 로드밸런서를 등록하셨다면 "www.도메인"으로 접근하셔야만 접근이 가능합니다.

www를 빼고 접근을 해도 해당 도메인에 접근할 수 있도록 설정하기 위해서 서브도메인을 비우고 별칭을 이용해서 도메인과 연결합니다.

 

728x90
반응형

'AWS' 카테고리의 다른 글

AWS SAA-CO3 Certification 취득  (0) 2024.12.08
AWS ECS 이용한 서비스 배포  (0) 2024.04.28
S3 객체 대량 삭제 시 복구 방법  (2) 2023.11.03
반응형

출처: Freepik, 작가 vikayatskina

 

처음 다른 분들의 글들을 둘러보면서 회고록을 볼 때마다 나도 쓰고싶다 써야겠다는 생각을 매번 해 왔었다.
 "내가 회고를 쓸 정도로 올 한 해 열심히 살았나? 쓸 내용이 있을까?" 이런 생각에 작년에는 회고록을 작성하지 못했다. 하지만 이번에는 한 번 작성해 보려고 한다. 
이러한 작은 기록들이 쌓이다 보면 나에게 큰 재산이 될 걸 알기 때문에 어렵게 생각하지 말고 생각나는 대로 올해를 뒤돌아보면서 당장 눈 앞으로 다가온 내년의 2023년 목표를 세워야겠다.

 

2022년 신년 계획을 생각한게 엊그제 같은데 많은 시간이 지나 벌써 12월 중순이다.
마냥 한게 하나도 없다라고 생각을 했었는데 앉아서 생각해보니 나름 치열하고 열정적으로 발버둥친 한 해 였던것 같기도 하다.
하지만, 2022년 신년 목표로 정한 이직과 목표 금액 모으기는 구체적으로 짜지못해 많은 아쉬움이 남는다. 
자신만의 서비스 하는 회사로의 이직을 성공해 회사와 함께 성장하고 싶었던 목표와 무지성 소비를 막기위한 목표 금액 저축이었다.
자산 저축은 어렵지 않은 금액을 목표로 설정해서 성공했지만(목표 금액 미스, 무지성 투자 실패..하..) 이직은 실패하였다.

 

현재보다 더 좋은 커리어로 업그레이드하기 위해서 매일 시간이 날 때마다 조금이라도 개발 공부를 하고 회사 내에서도 나를 성장시킬 수 있는 좋은 환경으로 바꾸려고 많은 노력을 했음에도 불구하고 시장에서 인정을 받지 못한 것 같아 씁쓸하다..
그래도 과정에서 경험하고 얻은 건 분명히 있었다. 
CI, CD를 구성해서 서비스도 만들어보고 (온전히 내 힘으로), 꾸준히 Git에 잔디도 심어 보려고 하고, 기술 블로그 글도 포스팅해보려고 하고, 어떻게 하면 회사에서 더 나은 환경을 찾아가는 경험도 해보고.. 큰 성과는 없었지만 나름의 고민을 하고 고군분투해 보았다.

 

커리어 외적으로는 남들이 나를 어떻게 생각하는지 굉장히 신경 쓰면서 소심하게 지내왔는데 이러한 생각이 나의 발전에 큰 도움이 되지 않는다고 크게 생각한 한 해이기도 하면서 또한 삶의 방향을 가장 많이 고민한 해이기도 하다. 
"내 실력을 인정하고 지금의 편안함에 안주하고 제태크에 시선을 돌릴까? 제태크 공부할 시간이 어딨어 스펙 쌓아서 더 좋은 커리어로 업그레이드해야지"라고 불과 며칠 전까지도 고민했지만 이 고민이 부질없다는 것을 깨달았다. 
어디에서 나의 포텐이 터질지 모르니 꾸준히 모든 토끼를 잡으려고 노력해야 한다는 것을 알았다.
이 글을 마치고 신년 계획을 세우려 가겠지만 내 신년 목표 구성은 토끼를 잡기위한 문어발식 확장이 될 것 같다.
이렇게 올 한 해를 뒤돌아보니 정리도 되고 내년에 계획을 어떻게 구성하면 좋을지 여러 아이디어도 떠올라서 도움이 된다.
내년 목표를 보다 정량적으로 구체적으로 세워서 성과 있는 회고록으로 다시 돌아오도록 하겠습니다.
2022년 마무리 잘하시고 더 발전한 2023년 맞이하시길!  
 
정말 생각나는 대로 글을 작성해서 읽기 힘든 글 읽어 주셔서 감사드립니다~

728x90
반응형

'그냥글 > 회고' 카테고리의 다른 글

잘했다 2023!  (0) 2024.01.08

+ Recent posts