본문 바로가기

전체 글264

[NestJS] API 권한 검사와 리소스 접근 검사를 나누기 어떤 API를 실행할 수 있는지와 그 API로 어떤 데이터를 다룰 수 있는지는 다른 문제였다. 기능별 권한 키를 Guard에서 검사하고, 데이터가 속한 범위와 작성자 조건은 Service에서 확인하도록 나눈 과정을 정리했다.같은 수정 권한이어도 수정할 수 있는 데이터는 달랐다기존 API에 기능별 권한 검사를 붙이면서, 로그인 여부나 관리자 여부만으로 나누던 조건을 더 세밀하게 정리했다. 화면에서 버튼을 숨기는 것과 별개로 서버에서도 요청한 작업을 허용할지 판단해야 했다.권한 키만 확인하는 것으로는 부족했다. 한 사용자가 여러 작업 공간에 소속될 수 있고, 공간마다 부여받은 권한도 달랐기 때문이다.예를 들어 A 작업 공간에서는 기록을 수정할 수 있지만 B에서는 조회만 가능한 사용자가 있다. 이 사용자에게 .. 2026. 10. 18.
[NestJS] 와일드카드 프록시가 컨트롤러 루트 경로를 놓친 이유 하위 경로 요청은 정상적으로 전달되는데 같은 컨트롤러의 루트 요청만 404가 발생했다. NestJS 와일드카드 경로가 매칭하는 범위를 실제 라우터에서 확인하고, 루트와 하위 경로를 함께 프록시하도록 수정한 과정을 정리했다.시작하며API Gateway에는 특정 경로 아래의 모든 요청을 내부 서비스로 전달하는 프록시 Controller가 있었다.@Controller(['resources', 'api/resources'])export class GatewayResourceController { @All('*path') async proxy(@Req() req: Request) { return this.gatewayService.proxyToCore(req); }}GET /resources/42나 P.. 2026. 10. 17.
[NestJS] Decorator를 붙였는데 검증이 실행되지 않는 이유 같은 DTO를 사용하는 애플리케이션도 ValidationPipe 설정은 서로 달랐다. 이 차이를 확인하면서 Decorator를 읽는 실행 주체와 DTO, Service, Database의 검증 범위를 정리했다.시작하며NestJS 코드는 Decorator를 이용해 많은 내용을 선언한다. Java의 Annotation과 비슷하게 보일 수 있지만, 여기서는 TypeScript와 NestJS의 용어에 맞춰 Decorator라고 부른다.@Controller('items')export class ItemController { @Post() create(@Body() dto: CreateItemDto) { return this.itemService.create(dto); }}DTO에도 입력 조건을 선언한다.. 2026. 10. 11.
[NestJS] 여러 DB 연결에서 Repository를 정확히 주입하는 방법 여러 데이터베이스를 사용하는 NestJS 애플리케이션에서 Repository 주입 오류를 겪었다. 연결 이름이 DI 토큰에 어떻게 반영되는지 확인하고, 같은 토큰으로 테스트 대역을 등록한 과정을 정리했다.시작하며하나의 데이터베이스만 사용하는 동안에는 Repository 주입이 단순하다.constructor( @InjectRepository(Account) private readonly accountRepository: Repository,) {}하지만 시스템이 여러 애플리케이션과 데이터 저장소로 나뉘면 한 서비스가 서로 다른 연결의 데이터를 함께 다뤄야 할 수 있다.공통 데이터베이스├── 사용자├── 권한└── 운영 대상기능별 데이터베이스├── 작업├── 일정└── 처리 결과이때 생성자 매개변수의 T.. 2026. 10. 5.
[AI Agent] 서비스 안에 Agent를 넣을 때 Tool 실행과 비용 주체 나누기 외부 AI Agent에 API를 공개하는 방법을 검토한 뒤, 서비스 자체에 채팅형 Agent를 넣는 구조도 비교했다. AI가 요청한 기능을 어디서 실행하고, 모델 사용 비용과 데이터 권한을 누구의 책임으로 둘지 정리했다.시작하며처음에는 ChatGPT나 Claude 같은 외부 Agent가 서비스 API를 호출하게 만드는 방법을 검토했다. MCP 서버를 따로 두는 방식과, 로그인된 웹페이지에서 데이터 조회 같은 기능을 AI가 호출할 수 있도록 제공하는 방식을 비교했다.MCP에서는 AI가 호출할 수 있도록 제공하는 기능을 '도구(Tool)'라고 부른다. 이 글에서는 기존 서비스 API에 연결된 조회와 처리 기능을 뜻한다. 예를 들어 '기간별 통계 조회'라는 도구에는 기능 설명과 시작일, 종료일 같은 입력값 형.. 2026. 10. 4.
[Node.js] 영문 계정은 되는데 한글 계정만 실패한 HTTP 헤더 문제 로그인과 토큰 검증은 성공했지만 한글 표시 이름을 HTTP 헤더에 그대로 넣으면서 내부 요청 생성이 실패한 원인과 해결 과정을 정리했다.시작하며로그인은 정상적으로 완료됐지만 로그인 이후의 API 요청이 실패하는 문제가 있었다. 모든 사용자에게 발생하는 것도 아니었다.영문 이름 사용자 정상한글 이름 사용자 실패Token은 정상적으로 발급됐고 Gateway의 인증도 통과했다. 사용자 정보도 올바르게 조회됐다. 그런데 Gateway가 요청을 내부 서비스로 전달하는 과정에서 한글 사용자에게만 예외가 발생했다.원인은 인증이나 권한이 아니었다. Gateway가 인증된 사용자의 이름을 Custom Header에 그대로 넣었고, Node.js HTTP Client가 요청을 만들면서 Header Value의 한글 문.. 2026. 9. 27.
[AI Agent] AI 에이전트에 API를 열 때 MCP와 WebMCP를 나누어 본 이유 에이전트가 기능을 호출하는 방법보다 먼저, 누구의 권한으로 어느 데이터에 접근하는지 정리했다. 서버형 MCP와 WebMCP를 검토하면서 나눈 인증 경계와 도입 기준을 기록한다.시작하며웹에서 사용하던 조회 기능을 AI 에이전트에도 제공하려고 했다. 사용자가 화면에서 대상을 고르고 버튼을 누르는 대신, 에이전트가 필요한 데이터를 조회하도록 만드는 것이다.처음에는 기존 API 앞에 MCP 서버를 두고 도구를 등록하면 될 것처럼 보였다. 하지만 호출 경로를 그려 보니 도구 정의보다 먼저 결정해야 할 것이 있었다.에이전트가 누구를 대신하며, 그 사용자가 접근할 수 있는 데이터 중 어디까지 허용할 것인가.기존 서비스에서는 로그인한 사용자라도 모든 작업 공간을 조회할 수 없었다. 사용자와 작업 공간 사이에 접근 권한.. 2026. 9. 26.
[Architecture] 엣지 서버의 로컬 ID와 전역 ID 충돌을 피하는 방법 하나의 엣지 서버가 자기 데이터와 중앙 플랫폼의 다른 운영 단위 데이터를 함께 조회할 때, 같은 숫자를 가진 로컬 ID와 전역 ID를 API 경계에서 분리한 과정을 정리했다.시작하며엣지 서버는 인터넷 연결이 끊겨도 자기 데이터를 조회할 수 있도록 로컬 MySQL을 가지고 있다. 온라인일 때는 중앙 플랫폼을 통해 다른 운영 단위의 데이터도 조회한다.처음에는 요청의 site_id가 로컬 DB의 site.id와 같으면 자기 데이터, 다르면 다른 운영 단위의 데이터라고 판단했다.site_id == local site.id → 로컬 조회site_id != local site.id → 중앙에서 위치 확인 후 원격 조회단일 서버만 볼 때는 자연스러운 규칙처럼 보였다. 그러나 온라인 조회를 붙이면서 이 방식에는 식별자.. 2026. 9. 20.
[Server] 사용자 JWT와 장비 Token의 인증 경계를 분리하기 사람과 장비는 같은 API Gateway를 호출하지만 Credential의 발급 방식과 수명주기, 접근 범위는 다르다. 사용자 JWT와 장비 Token을 별도의 Guard로 나누고, 인증 결과에서 자원 범위를 파생한 과정을 정리했다.시작하며처음에는 외부 요청을 모두 Gateway의 사용자 인증으로 처리하는 구성이 단순해 보였다. 브라우저 요청은 Access Token을 검증하고, 필요하면 Refresh Token으로 재발급한 뒤 내부 서비스에 사용자 정보를 전달하면 된다.하지만 외부 호출 주체가 사람만 있는 것은 아니었다. 로그인 화면을 사용할 수 없는 무인 장비와 자동화 프로그램도 API를 호출해야 했다.장비에 사용자 계정을 발급해 JWT를 사용하게 만들 수도 있다. 그러나 사용자 JWT는 로그인, 로.. 2026. 9. 19.
[Refactoring] 레거시 API를 이식할 때 계약과 버그를 구분하는 방법 레거시 API를 새로운 서버로 옮기면서 외부 계약은 보존하고, 내부 구현은 재구성하며, 명확한 버그는 회귀 테스트와 함께 수정한 기준을 정리했다.시작하며기존 앱이 사용하는 API를 새 DB에 연결하는 호환 서버를 만들고, 레거시의 설정 관리 기능을 새 API로 옮겼다. 두 작업 모두 기존 코드를 읽는 것에서 출발했지만, 바꿀 수 있는 범위는 달랐다.기존 앱을 그대로 유지하는 경로는 요청과 응답을 보존해야 했다. 호출자도 함께 바꾸는 설정 관리 API는 엔드포인트를 역할별로 나눌 수 있었다. 이 차이를 먼저 정하지 않으면 같은 변경을 두고도 한쪽에서는 개선, 다른 쪽에서는 장애가 된다.이식 범위유지해야 하는 것바꿀 수 있는 것기존 앱을 유지하는 호환 서버앱이 보내는 요청, 읽는 필드와 타입, 기대하는 결과.. 2026. 9. 13.