취미/프로그래밍

React with Nest.js Architecture

Anything Doing OK 2025. 5. 22. 17:13

신규 CRM 구축을 위한 NestJS & React 아키텍처 설계 제안

부제 : 단일 서버 통합구성과 서버분리 독립구성을 기반으로

 

1. 서론

본 문서는 신규 CRM(Customer Relationship Management) 시스템 구축을 위해 NestJS(백엔드)와 React(프론트엔드)를 기반으로 하는 두 가지 주요 아키텍처 접근 방식, 즉 1. 단일 서버 통합 구성(배포) 2. 서버 분리 독립 배포를 비교 분석하고, 각 방식의 장단점, 적합한 시나리오, 그리고 구체적인 구현 고려 사항을 제시합니다. 이를 통해 프로젝트의 특성, 팀 구성, 확장성 요구사항 등을 종합적으로 고려하여 최적의 아키텍처를 선택하는 데 도움을 드리고자 합니다.

참고 자료:


2. 아키텍처 옵션 상세 분석

2.1. 옵션 1: 단일 서버 - 통합 배포 (Monolithic Frontend & Backend)

개념: NestJS 애플리케이션이 React 애플리케이션의 빌드된 정적 파일(HTML, CSS, JavaScript 번들)을 직접 서빙하거나, NestJS가 서버 사이드 렌더링(SSR) 또는 정적 사이트 생성(SSG)을 통해 React 컴포넌트를 렌더링하여 완성된 HTML을 클라이언트에게 전달합니다.

주요 구현 방식: React 빌드 결과물을 특정 경로(예: /)에서 서빙하고, API 요청은 특정 접두사(예: /api)를 통해 처리합니다.

// Nest.JS main.ts 또는 app.module.ts
import { Module } from '@nestjs/common';
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';
import { YourApiModule } from './api/your-api.module'; // API 라우팅 모듈

@Module({
  imports: [
    ServeStaticModule.forRoot({
      rootPath: join(__dirname, '..', '..', 'client', 'dist'), // React 빌드 결과물 경로 (Vite는 dist, CRA는 build)
      exclude: ['/api/(.*)'], // API 경로는 제외하여 NestJS 컨트롤러가 처리하도록 함
      // serveRoot: '/', // 기본값. 특정 경로로 서빙하고 싶다면 변경 (예: '/app')
    }),
    YourApiModule, // CRM API 모듈 등록
    // ... other NestJS modules
  ],
})
export class AppModule {}

 

상세 흐름도:

[ 사용자 브라우저 ]
       |
       | (HTTPS 요청: www.example.com/dashboard 또는 www.example.com/api/users)
       V
[ 통합 서버 (Nginx/Apache 등 리버스 프록시 - 선택 사항) ]
       |
       V
[ Nest.JS 애플리케이션 서버 (Node.js 환경) ]
       |
       |-- (요청 경로 분석) --> [ ServeStaticModule ] -- (경로가 '/' 또는 React 라우트인 경우)
       |      |                                            |
       |      |                                            V
       |      |                                     [ React 빌드 산출물 (index.html, main.js, main.css) ]
       |      |                                            |
       |      |                                            V (클라이언트로 전송)
       |      |
       |      `-- (경로가 '/api/*' 인 경우) --> [ Nest.JS API 라우터/컨트롤러 ]
       |                                             |
       |                                             V
       |                                          [ 서비스 로직, 데이터베이스 연동 등 ]
       |                                             |
       |                                             V
       |                                          [ 데이터베이스 / 캐시 / 외부 API ]
       |                                             |
       |                                             V
       |                                          [ API 응답 (JSON 등) ]
       |                                             |
       |                                             V (클라이언트로 전송)
       V
[ 사용자 브라우저에 결과 표시 / React 앱에서 API 응답 처리 ]

장점:

  1. 단순화된 배포 및 인프라:
    • 하나의 애플리케이션(NestJS)만 배포 및 관리.
    • 단일 서버 인스턴스, 도메인, SSL 인증서로 인프라 관리 단순화.
    • 초기 설정 및 배포 자동화(CI/CD) 파이프라인 구성이 상대적으로 용이.
  2. 인증/세션 관리 용이성:
  3. CORS 문제 없음: 프론트엔드와 백엔드가 동일 출처에서 서비스되므로 CORS(Cross-Origin Resource Sharing) 정책 설정이 불필요.
  4. 초기 개발 속도: 작은 팀이나 풀스택 개발자 중심의 환경에서는 단일 코드베이스 및 컨텍스트로 인해 초기 개발 속도가 빠를 수 있음.

단점:

  1. 강한 결합도:
    • 프론트엔드와 백엔드가 하나의 프로세스 및 배포 단위로 묶여, 한쪽의 문제(예: NestJS 서버 다운)가 전체 서비스 중단으로 이어짐.
    • 한쪽의 리소스 사용량 급증(예: API 요청 폭주)이 다른 쪽(정적 파일 서빙) 성능에 영향을 줄 수 있음.
  2. 개발 및 빌드 복잡성 증가 가능성:
    • React 빌드 결과물을 NestJS 프로젝트 내 특정 위치로 복사하거나, NestJS 빌드 프로세스에 React 빌드 단계를 통합해야 함. (예: package.json 스크립트 관리)
  3. 기술 스택 및 팀 분리 어려움:
    • 프론트엔드와 백엔드 라이브러리/프레임워크 버전 충돌 가능성.
    • 각 팀의 독립적인 기술 결정 및 작업 흐름에 제약.
  4. 서버 부하 증가 (특히 SSR 시):
    • SSR은 각 요청마다 서버에서 React 컴포넌트를 렌더링하므로 CPU 사용량 증가 및 응답 시간 지연 가능성. (캐싱 전략 필수)
    • 정적 파일 서빙 자체도 Node.js의 주 스레드를 일부 사용할 수 있으므로, 고 트래픽 상황에서는 전용 웹 서버(Nginx 등)보다 비효율적일 수 있음.
  5. 확장성 제약: 프론트엔드 서빙과 백엔드 API 로직이 분리되지 않아, 특정 부분만 독립적으로 확장하기 어려움. (예: API 서버만 스케일 아웃)

적합한 경우:

  • 프로토타입 또는 MVP(Minimum Viable Product) 개발.
  • 개발팀 규모가 작고, 풀스택 개발자 중심으로 구성된 경우.
  • 인프라 관리 복잡성을 최소화하고 빠른 초기 배포를 목표로 하는 경우.
  • 내부용 소규모 애플리케이션.

2.2. 옵션 2: 서버 분리 - 독립 배포 (Decoupled Frontend & Backend)

개념: React 프론트엔드 애플리케이션과 NestJS 백엔드 API가 서로 다른 서버 또는 동일 서버 내 다른 포트에서 독립적으로 실행되고 배포됩니다. 이들은 네트워크를 통해 API를 호출하여 통신합니다.

주요 구현 방식:

  • React 프론트엔드:
      • 개발: Vite 또는 Create React App 등의 개발 서버 사용 (예: localhost:5173).
      • 배포: 빌드된 정적 파일(HTML, CSS, JS)을 CDN(AWS CloudFront, Cloudflare), 정적 웹 호스팅 서비스(Vercel, Netlify, AWS S3 + CloudFront, GitHub Pages) 또는 Nginx/Apache와 같은 웹 서버를 통해 배포.
  • NestJS 백엔드 API:
    • 개발: 독립적인 Node.js 서버로 실행 (예: localhost:3000).
    • 배포: Docker 컨테이너, PaaS(AWS Elastic Beanstalk, Google Cloud Run), IaaS(AWS EC2) 등에 배포.
    • API 엔드포인트: api.example.com 또는 리버스 프록시를 통해 www.example.com/api로 구성.
    • CORS 설정 필수.
// Nest.JS main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';

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

  // CORS 설정
  app.enableCors({
    origin: [
        'http://localhost:5173', // 로컬 React 개발 서버 (Vite 기본 포트)
        'http://localhost:3000', // 로컬 React 개발 서버 (CRA 기본 포트)
        'https://crm-app.yourdomain.com', // 실제 배포될 프론트엔드 도메인
        // 필요한 경우 추가 도메인/출처 등록
    ],
    methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
    credentials: true, // 인증 정보(쿠키 등)를 주고받아야 할 경우 true
  });
  // 참고: NestJS CORS 문서: https://docs.nestjs.com/security/cors

  // API 경로 접두사 설정 (선택 사항이지만 권장)
  app.setGlobalPrefix('api/v1');
  // 참고: NestJS 접두사 설정: https://docs.nestjs.com/faq/global-prefix

  // 전역 ValidationPipe 적용 (DTO 유효성 검사)
  app.useGlobalPipes(new ValidationPipe({
    whitelist: true, // DTO에 정의되지 않은 속성 제거
    forbidNonWhitelisted: true, // DTO에 정의되지 않은 속성 요청 시 에러 발생
    transform: true, // 요청 데이터를 DTO 타입으로 자동 변환
  }));
  // 참고: NestJS Validation: https://docs.nestjs.com/techniques/validation

  await app.listen(process.env.PORT || 3001); // 백엔드 서버 포트
  console.log(`Application is running on: ${await app.getUrl()}`);
}
bootstrap();

상세 흐름도:

[ 사용자 브라우저 ]
       |
       | (HTTPS 웹 페이지 요청: https://crm-app.yourdomain.com)
       V
[ 프론트엔드 서버 / CDN (예: Vercel, Netlify, AWS S3 + CloudFront) ]
       |  (React 정적 파일: index.html, main.js, main.css 등 서빙)
       V
[ 사용자 브라우저 - React 앱 로드 및 실행 ]
       |
       | (데이터 필요시 HTTPS API 요청: https://api.yourdomain.com/v1/users 또는 https://crm-app.yourdomain.com/api/v1/users (리버스 프록시 사용 시))
       V
[ (선택) API Gateway (예: AWS API Gateway, Nginx 등) ]
       |  (라우팅, 로드밸런싱, 인증, 요청 제한 등 역할 수행)
       V
[ (선택) 로드 밸런서 (예: AWS ALB/NLB) ]
       |
       V
[ Nest.JS 백엔드 API 서버 인스턴스들 (Node.js 환경) ]
       |  (API 로직 실행)
       |    |
       |    V
       |  [ 서비스 로직, 데이터베이스 연동 등 ]
       |    |
       |    V
       |  [ 데이터베이스 / 캐시 / 외부 API ]
       |    |
       |    V
       |  (API 응답 - JSON 등)
       V
[ 사용자 브라우저 - React 앱에서 API 응답 수신, 데이터 사용 및 UI 업데이트 ]

장점:

  1. 높은 유연성 및 확장성 :
    • 프론트엔드와 백엔드를 독립적으로 확장(scale out/up) 가능. 프론트엔드는 CDN으로 글로벌 배포, 백엔드는 API 트래픽에 맞춰 유연하게 조정.
    • 각 부분의 리소스(CPU, 메모리, 네트워크) 요구사항에 최적화된 인프라 구성.
  2. 관심사의 명확한 분리 :
      • 프론트엔드는 UI/UX, 백엔드는 비즈니스 로직/데이터 처리에 집중. 코드베이스 관리 용이.
      • 명확한 API 계약(예: OpenAPI/Swagger 명세)을 통해 팀 간 협업 원활.
  3. 독립적인 개발 및 배포 주기:
    • 프론트엔드팀과 백엔드팀이 각자의 기술 스택, 개발 도구, 배포 파이프라인, 릴리스 주기를 가질 수 있음.
    • API 계약이 유지되는 한, 한쪽의 변경이 다른 쪽에 즉각적인 배포 문제를 일으키지 않음.
  4. 기술 스택 선택의 자유:
    • 각 영역에 가장 적합한 기술, 라이브러리, 프레임워크 선택 가능.
    • 향후 특정 부분(예: 모바일 앱용 API, 특정 마이크로서비스) 추가 시 유연하게 대응.
  5. 마이크로서비스 아키텍처로의 전환 용이성: 백엔드가 이미 분리되어 있으므로, 필요에 따라 API를 여러 마이크로서비스로 분할하고 확장하기 용이.
  6. 최적화된 프론트엔드 제공: 정적 파일은 CDN을 통해 사용자에게 가장 가까운 엣지 로케이션에서 제공되므로 로딩 속도 극대화.

단점:

  1. CORS 설정 필수: 다른 출처(Origin)에서 서비스되므로 브라우저의 동일 출처 정책(Same-Origin Policy)으로 인해 API 요청 시 CORS 설정이 백엔드에 반드시 필요.()
  2. 인프라 복잡성 증가:
    • 프론트엔드 호스팅, 백엔드 서버, (선택적) API Gateway, CDN 등 여러 구성 요소 관리 필요.
    • 별도의 도메인/서브도메인, SSL 인증서 관리.
  3. 인증/인가 처리의 복잡성 증가 가능성:
    • 주로 토큰 기반 인증(JWT, OAuth2) 사용.
    • 쿠키 기반 세션을 사용하려면 서드파티 쿠키 문제, 도메인 및 경로 설정(예: SameSite=None; Secure, 부모 도메인 설정)에 대한 깊은 이해 필요. CSRF 방어 전략 추가 고려.
    • 참고: JWT 와 NestJS: https://docs.nestjs.com/security/authentication#jwt-functionality
  4. 개발 환경 설정의 복잡성:
      • 로컬 개발 시 프론트엔드 개발 서버(예: Vite)와 백엔드 API 서버(NestJS)를 각각 실행.
      • 프론트엔드 개발 서버에서 API 요청 시 CORS 문제를 해결하기 위한 프록시 설정 필요.
  5. 네트워크 지연 약간 증가: API 요청이 별도 서버로 가므로 동일 서버 내 통신보다 미세한 네트워크 지연 추가 (대부분의 경우 무시 가능).
  6. 엔드투엔드(E2E) 테스트 복잡성 증가: 여러 시스템을 연동하여 테스트 환경을 구성해야 함.

적합한 경우:

  • 중대규모 이상의 프로젝트, 서비스 지향 아키텍처(SOA)를 지향하는 경우.
  • 프론트엔드와 백엔드 팀이 분리되어 있거나, 독립적인 개발/배포/확장이 중요한 경우.
  • 높은 가용성, 확장성, 트래픽 처리 능력이 요구되는 서비스.
  • CDN을 통한 프론트엔드 성능 최적화가 매우 중요한 경우.
  • 향후 마이크로서비스 아키텍처로의 전환 가능성을 열어두고 싶은 경우.
  • 현대적인 웹 애플리케이션 개발에서 일반적으로 권장되는 방식.

3. 아키텍처 비교 요약

특징 1서버 (통합 배포) 2서버 (분리 배포)
배포 단위 단일 (NestJS 앱 + React 빌드 결과물) 다중 (NestJS 앱 따로, React 빌드 결과물 따로)
출처(Origin) 동일 (CORS 불필요) 분리 (백엔드 CORS 설정 필수, 로컬 개발 시 프론트엔드 프록시 설정 권장)
코드 공유 방식 내부적 (상대 경로, 모듈 임포트 등) 외부적 (API 호출)
빌드 의존성 높음 (프론트 빌드 -> 백엔드 패키징에 포함) 낮음 (API 계약만 준수)
CI/CD 파이프라인 통합 또는 순차적 (FE 빌드 후 BE 빌드/배포) 완전 분리 또는 독립적
결합도(Coupling) 높음 (Tight Coupling) 낮음 (Loose Coupling - API 계약 기반)
인프라 복잡성 낮음 중간 ~ 높음
확장성 유연성 낮음 높음
팀 독립성 낮음 높음
인증 (쿠키 기반) 간단 복잡 (토큰 기반 권장, 쿠키 사용 시 추가 설정 필요)
초기 개발 속도 빠를 수 있음 상대적으로 느릴 수 있음 (초기 설정)
장기적 유지보수성 낮아질 수 있음 높아질 수 있음

 


4. 추가 고려 사항 및 권장 사항

4.1. 프로젝트 초기 단계 vs. 장기적 관점
프로젝트 초기에는 옵션 1 (통합 배포) 이 빠른 프로토타이핑과 시장 검증에 유리할 수 있습니다. 그러나 CRM 시스템은 일반적으로 기능이 복잡해지고 데이터가 누적되며 사용자 수가 증가하는 경향이 있으므로, 장기적인 확장성, 유지보수성, 팀 분업 등을 고려할 때 옵션 2 (분리 배포) 가 더 적합한 선택이 될 가능성이 높습니다.

 

4.2. 개발팀 구성 및 전문성
팀이 풀스택 개발자 위주이고 규모가 작다면 옵션 1로 시작할 수 있습니다. 프론트엔드와 백엔드 전문 팀이 분리되어 있다면 옵션 2가 자연스럽습니다. 옵션 2는 각 팀이 자신의 전문 분야에 집중하여 개발 생산성을 높일 수 있습니다.

 

4.3. 인증 및 보안

  • 옵션 1(통합 배포): HttpOnly, Secure, SameSite=Lax (또는 Strict) 쿠키를 사용하여 세션 관리가 비교적 간단하고 CSRF 공격에 대해 상대적으로 안전합니다.
  • 옵션 2(분리 배포): JWT(JSON Web Token)를 사용하는 것이 일반적입니다.

4.4. 개발 환경 (DX - Developer Experience)

  • 옵션 1: 단일 프로젝트 설정, 하나의 npm start 명령으로 실행 가능 (단, 빌드 스크립트 통합 필요).
  • 옵션 2: 프론트엔드, 백엔드 각각 개발 서버 실행.

4.5. 코드 구조 (Monorepo vs. Polyrepo)

  • 옵션 1: 자연스럽게 단일 레포지토리(Monorepo의 한 형태)가 됩니다.
  • 옵션 2: 프론트엔드와 백엔드를 별도의 레포지토리(Polyrepo)로 관리하거나, Nx, Lerna, Turborepo 같은 도구를 사용하여 Monorepo로 관리할 수 있습니다. Monorepo는 코드 공유, 통합 테스트, 버전 관리 등에서 이점이 있을 수 있지만, 초기 설정과 빌드/CI 복잡성이 증가할 수 있습니다.

4.6. CI/CD (지속적 통합/배포)

  • 옵션 1: 단일 파이프라인. React 빌드 후 NestJS 애플리케이션에 통합하여 배포.
  • 옵션 2: 프론트엔드와 백엔드 각각 독립적인 CI/CD 파이프라인 구성. 프론트엔드는 정적 호스팅/CDN으로, 백엔드는 서버/컨테이너 환경으로 배포.

4.6. 폴더 구조의 차이점

주요 폴더 구조 차이점 비교

특징 옵션 1: 단일 서버 - 통합 배포 옵션 2: 서버 분리 - 독립 배포 (Polyrepo 기준)
프로젝트 루트 단일 루트 (crm-integrated-app/) 프론트엔드, 백엔드 각각 독립된 루트 (crm-frontend/, crm-backend/)
프론트엔드 위치 백엔드 프로젝트의 하위 디렉토리 (client/) 또는 빌드 결과물만 특정 위치에 복사 독립적인 프로젝트 폴더 (crm-frontend/)
백엔드 위치 프로젝트의 주축 (server/ 또는 프로젝트 루트) 독립적인 프로젝트 폴더 (crm-backend/)
node_modules - React용, NestJS용 각각 존재 가능 (모노레포 도구 없이는 분리)<br/>- 모노레포 도구 사용 시 최상위 node_modules 공유 가능 프론트엔드, 백엔드 각각 완전히 독립된 node_modules/
package.json - client/package.json, server/package.json 각각 존재<br/>- (선택) 최상위 package.json (모노레포 도구 사용 시) crm-frontend/package.json, crm-backend/package.json 각각 존재
빌드 결과물 처리 React 빌드 결과물이 NestJS 프로젝트 내 특정 폴더(server/dist/client 등)로 복사되거나, NestJS가 직접 참조하여 서빙 - React: 독립적인 빌드 결과물 (dist/)을 정적 호스팅/CDN에 배포<br/>- NestJS: 독립적인 빌드 결과물 (dist/)을 서버에 배포
코드 공유 방식 - React -> NestJS: 불가능 (API 호출)<br/>- NestJS -> React: 불가능 (API 응답)<br/>- 동일 프로젝트 내 타입/유틸리티 공유는 상대 경로로 가능하나 권장되지 않음. API 계약(명세)을 통한 완전한 분리. 코드 직접 공유 없음 (단, 타입 정의 등은 별도 패키지로 만들어 공유 가능)
설정 파일 (예: tsconfig.json, .eslintrc.js) client/, server/ 각각 존재 crm-frontend/, crm-backend/ 각각 존재
개발 환경 실행 단일 npm start (내부적으로 React, NestJS 서버 동시 실행 스크립트 구성) 또는 NestJS 서버만 실행 (React는 빌드 후 서빙) 프론트엔드 서버, 백엔드 서버 각각 별도 터미널에서 npm start

핵심 차이점 요약

  1. 프로젝트 결합도 및 독립성:
    • 옵션 1 (통합): 프론트엔드와 백엔드가 하나의 프로젝트 단위로 강하게 결합되어 있습니다. client 폴더는 server 폴더의 일부처럼 취급될 수 있습니다. 배포도 하나의 단위로 이루어집니다.
    • 옵션 2 (분리): 프론트엔드와 백엔드가 완전히 독립된 프로젝트입니다. 각자의 생명주기(개발, 빌드, 배포)를 가집니다. API를 통해 통신하며 서로의 내부 구현에 대해 알 필요가 없습니다.
  2. 관리 단위:
    • 옵션 1 (통합): 단일 저장소(repository)에서 관리되는 경우가 많습니다. 빌드 및 배포 파이프라인도 통합될 가능성이 높습니다.
    • 옵션 2 (분리 - Polyrepo): 프론트엔드와 백엔드가 각각 별도의 저장소에서 관리될 수 있습니다. CI/CD 파이프라인도 분리됩니다.
    • 옵션 2 (분리 - Monorepo): 단일 저장소 내에 여러 독립적인 프로젝트(apps/)를 두고, 공유 가능한 코드(libs/ 또는 packages/)도 함께 관리합니다. 모노레포 도구(Nx, Turborepo 등)가 이를 효율적으로 관리하도록 돕습니다.
  3. 코드 공유:
    • 옵션 1 (통합): 직접적인 코드 공유는 어렵고 권장되지 않지만, 이론적으로는 같은 파일 시스템 내에 있으므로 가능성은 열려 있습니다. (주로 타입 공유 정도)
    • 옵션 2 (분리 - Polyrepo): 코드 직접 공유는 불가능합니다. API 명세를 통해 상호작용합니다. 만약 공통 로직이나 타입이 필요하다면, 별도의 npm 패키지로 만들어 각 프로젝트에서 의존성으로 추가해야 합니다.
    • 옵션 2 (분리 - Monorepo): 모노레포의 가장 큰 장점 중 하나로, libs/ (또는 packages/) 디렉토리를 통해 프론트엔드와 백엔드 간에 타입 정의(DTOs), 유틸리티 함수, 심지어 UI 컴포넌트까지 손쉽게 공유할 수 있습니다.

 


5. 최종 권고안

신규 CRM 시스템은 일반적으로 지속적인 기능 추가와 사용자 증가가 예상되는 서비스입니다. 따라서 장기적인 확장성, 유지보수성, 팀 간 협업 효율성, 기술적 유연성을 고려할 때, 옵션 2: 서버 분리 - 독립 배포 아키텍처를 채택하는 것을 강력히 권장합니다.

주요 사유:

  1. 확장성: 프론트엔드와 백엔드의 트래픽 및 리소스 요구사항이 다를 수 있으므로, 각각 독립적으로 확장하여 비용 효율성과 성능을 최적화할 수 있습니다.
  2. 팀 생산성: 프론트엔드팀과 백엔드팀이 각자의 전문 영역에 집중하고, 독립적인 개발 및 배포 주기를 가져갈 수 있어 개발 속도와 품질을 높일 수 있습니다.
  3. 기술 선택의 유연성: 향후 특정 기술 스택의 업그레이드나 변경 시, 다른 부분에 미치는 영향을 최소화할 수 있습니다.
  4. 견고성(Resilience): 한쪽 시스템의 장애가 다른 쪽 시스템에 직접적인 영향을 미치는 것을 방지할 수 있습니다 (예: 백엔드 API 서버 점검 중에도 캐시된 프론트엔드는 계속 서비스 가능).

물론, 옵션 2는 초기 설정의 복잡성(CORS, 인증, 다중 배포 파이프라인 등)이 존재하지만, 이는 잘 정립된 패턴과 도구(Docker, CI/CD 서비스, API Gateway 등)를 통해 관리 가능하며, 장기적인 이점을 고려할 때 충분히 감수할 만한 수준입니다.