본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 05. 20. 19:01

【Claude Code】매서운 리뷰어 SKILL ~모르는 코드를 PR에 올리지 마라~

요약

AI 코딩 에이전트의 발전으로 구현 속도는 빨라졌으나, 코드를 완전히 이해하지 못한 채 PR을 올리는 리스크가 커지고 있습니다. 이를 방지하기 위해 Claude Code를 활용하여 스스로 구현 원리와 아키텍처를 집요하게 검증받는 '매서운 리뷰어' 워크플로우를 제안합니다.

핵심 포인트

  • AI 생성 코드에 대한 셀프 리뷰 부족은 유지보수 및 버그 대응 능력을 저하시킴
  • 단순 구현을 넘어 '왜 이 아키텍처와 설정을 선택했는가'에 대한 설명 능력이 필요함
  • AI를 단순 구현 도구가 아닌, 구현 원리를 학습하게 만드는 질문자로 활용해야 함
  • 질문받고 답변하는 사이클을 통해 엔지니어로서의 학습 기회를 확보할 수 있음

피부로 느껴지는 감각으로는 Opus 4.5 정도부터 성능이 급격히 향상되어, 광범위한 변경을 원활하게 실시할 수 있게 되었습니다.

편리한 한편, 코드를 손으로 직접 쓰던 시절에 비해 구현에 대한 이해도가 떨어지고 있지는 않은지 불안함이 있습니다.

그러한 상황 속에서는 AI에 의해 생성된 코드 중에는 셀프 리뷰 (Self-review)가 불충분하거나, 동작을 이해하지 못한 것이 섞여 있을 리스크가 있습니다.

PR (Pull Request)의 내용은 "왜 이렇게 구현했는가", "이 아키텍처 (Architecture)를 선택한 이유는 무엇인가" 등을 설명할 수 있어야 한다고 생각합니다.

"왠지 동작하니까 올립니다!"는 리뷰어에게도, 나중에 유지보수할 미래의 자신에게도 실례겠지요.

코딩 에이전트로는 Claude Code를 사용하고 있습니다.

지식의 누락 해소- 경력직 입사나 보직 변경 등으로 기존 서비스 개발에 중간에 참여하는 경우, CI · 테스트 코드 · 아키텍처 등 이미 어느 정도 기반이 갖춰져 있는 경우가 많습니다.

그 상태에서 이해가 따라가지 못한 채 AI에게 "◯◯을 참고해서"라고 구현을 맡기면, 구조를 이해하지 못한 코드가 쌓여가게 됩니다.

  • 경력직 입사나 보직 변경 등으로 기존 서비스 개발에 중간에 참여하는 경우, CI · 테스트 코드 · 아키텍처 등 이미 어느 정도 기반이 갖춰져 있는 경우가 많습니다.

AI에 대한 완전 의존을 피하고, 구현에 대한 이해의 기회를 확보하기- 적혀 있는 코드를 읽는 것보다, 스스로 조사하거나 직접 손을 움직여 쓰는 편이 이해가 더 잘 되는 법입니다. 그렇다고는 해도 AI를 사용하고 싶으므로, 적어도 "제대로 이해하고 있는가?"를 AI가 집요하게 물어보게 함으로써 이해의 기회를 되찾습니다.

구현 방침이나 아키텍처, 설정 내용에 대해 적절한 판단을 할 수 있도록 하기- "어쩌다 보니 이걸로 동작했다"가 아니라, "다른 선택지와 비교하여 이것을 선택했다"라고 설명할 수 있는 상태를 목표로 합니다.

구현 방침을 세운다 → Claude에게 방침대로 구현하게 한다 → 완성.

이제, "아마존 같은 EC 사이트를 만들어줘!"라고 하면 만들어져 버리겠지요.

제가 손으로 2, 3일 걸려 작성하던 규모의 변경도, 최근에는 반나절~하루 정도면 완성되는 일이 늘었습니다. AI 정말 대단하네요.

한 번에 구현할 수 있는 양이 늘어났기 때문에, 자신이 이해하지 못한 코드가 슬그머니 섞여 들어가는 일이 발생하게 되었습니다.

  • "왠지 돌아가는데... 이 설정값은 왜 필요했더라?"
  • "이 파일, 왜 여기에 두었더라?"
  • "이 에러 핸들링 (Error handling), 언제 들어갔지?"

PR을 올리기 직전에 셀프 리뷰를 하고 있으면, 이런 의문이 자주 떠오릅니다.

모르겠지만 동작하니까 오케이!

당연히 좋지 않겠지요.

PR 리뷰에서 대답할 수 없다 → 리뷰어의 시간을 뺏게 됨
버그 발생 시 조사가 늦어진다 → 자신이 올린 PR임에도 불구하고 동작을 파악하지 못하고 있어 1차 조사가 남의 일처럼 느껴짐
배움이 쌓이지 않는다 → "구현을 통해 이해한다"라는 엔지니어로서 가장 큰 학습 기회를 그냥 지나치게 됨

AI에게 전적으로 맡긴 만큼, 본래 얻었어야 할 학습이 결여된 채 코드만 늘어가는 상태입니다.

이것은 좋지 않다고 생각하여, PR 작성 전의 플로우 (Flow)를 손보기로 했습니다.

지금까지도 PR을 올리기 전에 AI에게 리뷰를 시키고 있었지만, 자기 자신이 이해하고 있는지를 집요하게 따지게 하는 방식은 하지 않았습니다.

손으로 코드를 쓰는 기회가 적어진 지금, 구현에 대한 이해를 깊게 하는 데 있어 "집요하게 질문받기" → "조사해서 답변하기"의 사이클은 유효하다고 생각합니다.

사람을 상대로 질문을 받으면 심리적 비용이 높지만, AI를 상대로라면 부담 없이 질문을 받을 수 있다는 것도 장점입니다. 몇 번이고 "모르겠다"라고 말해도 문제없습니다.

그래서, 매서운 리뷰어 SKILL을 만들어 보았습니다.

Claude Code의 SKILL로서 작성해 보았습니다.

대략적인 내용은 다음과 같습니다.

diff를 스스로 읽기: 브랜치의 변경 내용을 AI가 먼저 파악함 -
예정 질문 수를 미리 결정: diff의 규모와 복잡도에 따라 타당한 질문 수를 추산하여 처음에 제시함. /gekidume-review 5와 같이 인수로 고정할 수도 있음 -
한 번에 하나씩 질문하기: 한꺼번에 질문을 받으면 사고가 분산되므로 입도를 세밀하게 함. 각 질문의 머리에 Q3/10 표시

진척 상황을 알 수 있는 번호(예: Q3/10)를 붙인다.
틀렸을 경우 선택지 제시: 정답을 통째로 알려주지 않고, 여러 선택지 중 하나를 고르게 하여 스스로 생각하게 한다.
OK 판정 기준 보유: 구현 내용, 선정 이유, 부작용(Side Effect), 엣지 케이스(Edge Case)의 4가지 관점을 충족할 때까지 PR(Pull Request) 생성 OK를 내지 않는다.
리뷰 기록 출력: OK를 낸 후, 질문과 답변의 주고받은 내용을 GEKIDUME_REVIEW.md로 정리하여 프로젝트 루트에 출력한다. PR 설명문에 복사해서 붙여넣거나 회고할 때 사용할 수 있다 (commit 대상 제외).
도중에 구현 변경으로 전환 가능: 압박 질문을 받는 도중에 "역시 구현을 수정하고 싶다"라고 판단되면, 질문을 중단하고 일반 구현 모드로 돌아간다.

특히 "틀렸을 경우 선택지 제시" 부분이 중요한데, AI에게 바로 정답을 알려달라고 하면 이해가 정착되지 않기 때문에, 선택지 중에서 스스로 고르게 하고 있습니다.

---
name: gekidume-review
description: PR 생성 전에, diff에 포함된 변경 내용의 이해도를 엄격하게 검증한다. 구현 이유, 아키텍처 선정, 동작 원리를 질문 공세로 확인하여, 사용자가 완전히 이해하지 못한 코드를 PR에 포함하는 것을 방지한다. PR을 만들기 전의 최종 확인, diff 이해도 체크, "격하게 몰아붙이는 리뷰(激詰めレビュー)", "gekidume-review", "PR 전에 검증하기", "grill me before PR" 등이 언급되었을 때 사용한다.
...

Q3/10: [filename:L42]의 이 조건 분기는 왜 필요합니까? 없애면 어떻게 됩니까?

예정 질문 수가 도중에 변경된 경우에는 그 시점의 값을 반영한다 (예: 7문제를 예정했으나 추가로 1문제가 늘어나면 `Q8/8`로 표시).
### 4. 답변의 평가
사용자의 답변을 평가할 때는 다음을 따른다:
...
```markdown
# 격하게 몰아붙이는 리뷰 기록
## 개요
- 대상 브랜치: <branch>
- 베이스 브랜치: <base>
- 실시 일시: <YYYY-MM-DD HH:MM>
- 예정 질문 수: <초기값> → <최종값> (변동이 있는 경우에만 둘 다 기재)
## 변경 요약
diff의 요점을 3~5행으로 요약.
## 질문과 답변
### Q1/N: <질문 문구>
**답변:** <사용자 답변의 요지>
**평가:** <충분함 / 심층 질문 있음 / 선택지 제시로 수정 등, 간결하게>
(모든 질문에 대해 동일한 형식으로 나열)
## 도중의 방침 변경・구현 수정
질문 도중에 구현을 수정했거나 방침을 바꾼 부분이 있다면, 경위를 시계열로 기재한다. 없다면 "없음"이라고 적는다.
## 최종 판정
PR 생성 OK. 특이 사항이 있다면 여기에.

파일을 생성하면 사용자에게 "GEKIDUME_REVIEW.md를 생성했습니다 (commit 대상 제외)"라고 한마디 전달한다.

7. 질문 중단 (구현 변경으로의 전환)

사용자가 질문 내용을 조사하는 도중에, 현재의 구현이 의도와 다르거나 다른 방침으로 변경하고 싶다고 판단하는 경우가 있다. 그 경우에는:
...


`/gekidume-review`로 실행 가능합니다. 바쁠 때처럼 질문 수를 고정하고 싶다면, `/gekidume-review 5`와 같이 숫자를 전달하여 지정할 수 있습니다.

*압박 질문을 받습니다*

실험적으로 구현하고 있던, 어떤 Vector 검색을 이용하는 MCP 서버 구현의 PR 생성 전에 gekidume-review를 실행했습니다.

그때 리뷰어(AI)로부터 던져진 질문을 발췌하여 소개합니다. 고유명사, ID, 경로 등은 범용화하였습니다.

대략적으로,

- 보안 측면의 질문
- 명명(Naming) 및 구현 내용의 의도
- 설정 내용에 관한 설명
- 엣지 케이스(Edge Case)의 동작에 관한 질문

등이 제시됩니다.

-
**Q1/8: 검증용 token을 query string에 넣는 리스크**-
**Q**: 애플리케이션 계층의 token 검증만이 방어라는 구성에서, 여전히 남아있는 토큰 유출 경로를 한 가지 들 것 -
**A**: 액세스 로그, Referer 등으로 유출됨. MCP 클라이언트 측의 커스텀 헤더 전송 기능으로 헤더를 통한 토큰 전송이 가능함을 확인했으므로, middleware를 "헤더 우선 / query는 하위 호환성을 위해 남겨둠" 순서로 수정

-

**Q2/8: Lambda Function URL 공개 설정 하에서의 폭주 리스크**-
**Q**: `authorization_type = "NONE"` (누구나 액세스 가능) + CORS + `principal = "*"`

의 공개 구성에서, 악의적/비악의적 양쪽 케이스의 "반갑지 않은 일" 2가지 - 
**A**: 토큰(token) 없이도 도달하는 요청으로 인한 비용 증가, bot의 무차별 스캔 등. Lambda 동시 실행 수를 3으로 제한하는 가벼운 보험을 도입한다.

-
-
**Q3/8: 인증의 신뢰 경계 (Trust Boundary)**-
**Q**: 본 MCP용 로그인 토큰을 가진 전원이 약 600건의 전체 티켓 본문을 읽을 수 있는 권한 모델이 업무 정책상 OK인가 - 
**A**: 토큰 발행이 admin 관리 화면 측에서 권한 게이트(gate)되어 있다는 전제하에, 예상 이용 범위와 어긋남이 없음. MCP 호출 측에서 추가적인 액세스 제어(Access Control)는 불필요.

-
-
**Q4/8: 공통 DB 명명 이유**-
**Q**: 왜 서비스 전용 DB가 아니라, 여러 서비스 공통의 벡터 검색용 DB로 했는가 - 
**A**: 타 서비스의 데이터를 향후 넣을 가능성에 대비한 공통 명명. DB 분리는 관리 비용 증가를 초래하므로 스케일(scale) 이후에 재고. 테이블에는 서비스 이름의 prefix를 붙여 구분.

-
-
**Q5/8: Cohere `input_type`을 저장 시와 검색 시로 구분하여 사용하는 이유**-
**Q**: 저장 시 `search_document` / 검색 시 `search_query`를 통일했을 경우 어떤 품질 차이가 발생하는가 - 
**A**: Cohere embed-v3는 `input_type`마다 별도의 prefix를 부여하여 별도의 공간에 임베딩(embedding)하는 사양. 통일할 경우 길이 편향(length bias)으로 인해 검색 정밀도가 저하됨.

-
-
**Q6/8: 글자 수 하드 캡(Hard Cap)을 통한 강제 절단이 필요한 이유**-
**Q**: 구현된 메서드의 인자인 `chunk_size`의 단위는 무엇인가? 설정한 `chunk_size`를 초과한 2364자의 청크(chunk)가 생성된 원인, 후속 단계에 상한을 초과할 경우 반올림/절단(rounding) 처리를 추가한 판단 이유 - 
**A**: `chunk_size`는 토큰(token) 단위. 구현상, 마침표가 없는 일본어 장문은 단문으로 분할되지 않아 `chunk_size`를 초과하는 청크가 생성됨. 의미 경계를 깨뜨리지 않기 위해, 절단 처리는 예외적인 보험 역할로 남겨두고자 후속 단계에 배치.

-
-
**Q7/8: TiDB Cloud 클러스터 식별자 prefix의 템플릿(template)화 + 32자 제한 대응**

※ 사용자 정의 SQL 파일에서는, `CREATE USER IF NOT EXISTS '<CLUSTER_PREFIX>user_name'@'%' IDENTIFIED BY '<ENTER_STRONG_PASSWORD_1>';`와 같이 템플릿화했습니다. -
**Q**: 클러스터 식별자의 prefix를 템플릿화한 이유 - 
**A**: GitHub 관리상의 정보 노출 최소화와 환경 재생성 시의 수정 부담을 줄이기 위해 템플릿화.

-
-
**Q8/8: IAM 최소 권한 + 공유 역할 (Shared Role)**-
**Q**: SSM Parameter 쓰기 권한 사용자의 인증 정보를 Resource에 포함하지 않는 설계의 장점 - 
**A**: 계층 방어(Defense in Depth)로서 읽기 전용 권한만 부여하여, 역할(role) 유출 시의 데이터 파괴 리스크를 배제.

-

## 상세(질문문·배경·답변) 보기

| 화자 | 아이콘 | 역할 |
|---|---|---|
| 리뷰어 | |
🎙️ | 엄격한 리드 엔지니어 역할. 질문·평가·지적 담당 |
| 나 | |
📝 | 구현자. 질문에 답변하는 측 |

**🎙️ 리뷰어**

인증 메커니즘을 보겠습니다.

-
`middleware.go`: query string `?user_token=xxx`를 `X-User-Token` 헤더로 전기(transfer)
- `auth.go`: `InternalSSOService.DecryptAppToken`으로 검증
- `Lambda 측 Terraform 설정 내용`:
  - Function URL
    - `authorization_type = "NONE"`
    - CORS `allow_origins = ["*"]`
  - `aws_lambda_permission`의 `principal = "*"`
  - Function URL

즉 **네트워크 계층은 전부 통과하고, 애플리케이션 계층의 `user_token` 검증만이 인증** 역할을 하는 구성입니다.

질문: **이 구성에서 `user_token`이 유출될 수 있는 주요 경로를 한 가지만 들어보세요.**

힌트: 「URL의 query string에 기밀 토큰을 넣을」 때, HTTPS로 보호되지 않는 계층이 있습니다.

**📝 나**

애플리케이션 계층 (Application Layer)에서는 보호되지 않아 액세스 로그 (access log)로 유출된다. 참고로, 요청 헤더 (request header)에 넣도록 변경할 수 있을까?

**🎙️ 리뷰어**

정답입니다. access log (CloudWatch / Function URL access log / 중간 프록시 (intermediate proxy)) + 브라우저 히스토리 + Referer 헤더 등에 query string이 남습니다.

**「요청 헤더로 변경할 수 있는지」**에 대해서는, 확인 후 답변하겠습니다. 기존의 유사한 MCP가 동일한 패턴을 채택하고 있는 이유 (Claude Web Custom Connector의 제약 사항)를 살펴보겠습니다.

(중략: 기존 MCP의 middleware를 확인하여, 서버 측은 이미 `X-User-Token` 헤더를 읽고 있음을 확인)

답변하겠습니다:

**결론: 현재로서는 「가능하지만, 클라이언트 측 (Claude Web Custom Connector)의 제약으로 인해 실질적으로 어렵다」입니다.**

**서버 측의 현황:**

- `auth.go`는 `X-User-Token` 헤더를 읽고 있음
- `middleware.go`가 query string을 헤더로 전사 (copy)하고 있을 뿐임
- 즉, **서버는 헤더 수신에 이미 대응 완료**된 상태. middleware를 제거하는 것만으로 query string 경유를 무효화할 수 있음

**클라이언트 (Claude Web Custom Connector) 측의 제약:**

- Claude Web의 「Custom Connector」는 URL 하나를 등록하는 UI로, 커스텀 헤더 (custom header)를 보낼 수 있는 기구가 없음 (OAuth 2.0 플로우를 구성하지 않는 한)
- 그렇기 때문에 다른 MCP들도 query string 방식을 채택하고 있음
- Claude Desktop의 MCP 설정이라면 `headers` 필드로 헤더 전송이 가능하지만, 이번에 상정된 클라이언트는 Claude Web임

**적절한 대응안 (강한 순서대로):**

- (A) **MCP OAuth 2.0 플로우 대응** — Claude Web이 서버 측의 인가 엔드포인트 (authorization endpoint)를 호출하여, 수명이 짧은 access token을 Bearer로 전송함. 이번 PR 범위를 크게 벗어남
- (B) **현상 유지 + 로그 위생 대책** — CloudWatch Logs의 query string을 마스킹(masking) 처리, 액세스 로그를 단기간 내에 파기, 토큰 자체를 단기화 (`InternalSSOService.DecryptAppToken`이 이미 `IsExpire()`를 체크 중)
- (C) **middleware를 유지하면서, 폴백 (fallback) 경로로서 Authorization 헤더도 수용** — 향후 헤더 대응 클라이언트가 등장했을 때 유용하게 사용할 수 있음

어떻게 할까요?

**📝 나**

다음과 같이 `claude.json` 등에 기재한다는 전제입니다. 헤더를 추가할 수단이 없다면 변경 없이 OK. 해당 부분에 그 취지를 주석으로 남겨두세요.

```json
"app-mcp": {
"command": "~/.nodenv/shims/npx",
"args": [
...

🎙️ 리뷰어

mcp-remote (npx 패키지)는 사실 커스텀 헤더 전송을 지원합니다. --header 옵션으로 다음과 같이 작성할 수 있습니다:

"app-mcp": {
"command": "~/.nodenv/shims/npx",
"args": [
...

이렇게 하면 토큰은 Authorization에 가까운 방식으로 헤더에 들어가며, URL의 액세스 로그에는 남지 않습니다. 만일을 위해 확인하겠습니다.

(mcp-remote의 소스를 grep 하여, --header "Key:Value" 파서 (parser)가 존재하는지 확인)

확인 완료: mcp-remote의 소스에 --header "Key:Value" 정규 표현식 파서가 있으며, 헤더 전송은 지원됩니다.

그 점을 바탕으로, 코드 측에서 발견한 점이 하나 있습니다:

middleware.gor.Header.Set("X-User-Token", token)

쿼리 스트링 (query string)을 항상 우선하여 헤더를 덮어씁니다. 헤더 (header) 권장 방식으로 기울이려면, 헤더가 비어 있을 때만 쿼리 (query)를 확인해야 합니다:

if r.Header.Get("X-User-Token") == "" {
if token := r.URL.Query().Get("user_token"); token != "" {
r.Header.Set("X-User-Token", token)
...

판단을 부탁드립니다:

  • (A) 헤더 우선 순위 수정 + 코멘트로 --header 권장을 명시한다 (권장, 경미한 수정)
  • (B) 코멘트만 추가한다 (순서는 그대로 유지)
  • (C) 변경 없이 PR을 제출한다
  • (D) 하는 김에 다른 유사한 MCP의 미들웨어 (middleware)도 동일하게 수정한다 (동일한 형태이므로)

📝 나

대상 MCP는 헤더 우선으로 해주세요. A입니다.

유사한 MCP에는 향후 맞추겠다는 내용을 TODO로 기재해 두세요. 단, 권장 사항이며, 쿼리 스트링 (querystring)도 하위 호환성을 위해 남겨두고 싶습니다.

추가로, README에 설정 예시를 작성해 두세요.

🎙️ 리뷰어

(middleware.go의 헤더 우선화, 유사 MCP에 대한 TODO 추가, README에 설정 예시 추가를 실시하고 커밋 (commit))

🎙️ 리뷰어

헤더 쪽으로 맞추어 토큰 (token) 유출 경로는 막았지만, 네트워크 계층 (network layer)의 방어는 여전히 제로입니다:

  • Function URL
    authorization_type = "NONE"

cors.allow_origins = ["*"]

aws_lambda_permission.principal = "*"

인터넷상의 누구라도 /mcp 엔드포인트 (endpoint)에 POST 요청을 날릴 수 있는 상태이며, 유일한 방어책은

TokenValidator.IsValidToken

입니다. 질문: 악의가 없는 경우와 악의가 있는 경우 모두, 이 구성에서 발생할 수 있는 "반갑지 않은 일"을 2가지 들어보세요.

힌트:

  • 토큰을 가지고 있지 않은 공격자라도, 요청 자체는 Lambda까지 도달합니다 - Lambda 실행 시간 × 호출 횟수 = AWS 과금

📝 나

없는 경우

잘 상상이 안 됩니다.

있는 경우

token이 없어도 대량으로 요청을 실행함으로써 비용 증가로 이어질 수 있습니다.

🎙️ 리뷰어

"있는 경우"가 정답입니다. "없는 경우"의 선택지를 제시하겠습니다:

  • (a) botnet / 보안 스캐너 (shodan 등)의 무차별 스캔. Lambda Function URL은 전 세계의 스캐너에 의해 상시 발견 및 프로브 (probe)되고 있습니다 → 의도치 않게 Lambda 기동 및 과금 발생
  • (b) 사내 멤버가 URL을 Slack / Notion 등에 실수로 붙여넣기 / GitHub public 리포지토리 (repository)에 혼입 → 누구나 접근 가능해짐
  • (c) 클라이언트 측 (Claude Desktop)의 설정 오류로 token이 빠진 상태로 계속 호출 → 실패 로그가 쌓임
  • (d) 로그 오염으로 401 실패 로그가 CloudWatch를 채워, 진짜 문제를 묻히게 함 / Lambda 에러율 기반의 알람이 상시 발생

위의 사항들은 전부 일어날 수 있는 이야기입니다.

Q5-판단.

여기까지를 바탕으로, 이대로 제출할지 판단해 주세요. 선택지:

  • (A) 이대로 제출한다 (다른 유사 MCP도 auth_type=NONE으로 운용 실적이 있다는 전제). 이 레이어에서의 비용 폭주는 Lambda 타임아웃 60s × 낮은 이용 빈도로 인해 실용상 문제가 되지 않는다고 판단
  • (B) Lambda Function URL의 authorization_typeAWS_IAM으로 변경한다 → 호출 측 (mcp-remote)도 SigV4 서명이 필요하게 되어, 클라이언트 설정이 훨씬 복잡해짐

를 변경한다 - (C)
AWS WAF를 Function URL 전단에 배치한다 (Rate Limit, IP Allowlist, 지리적 제한 등). 단, Function URL은 WAF에 직접 연결할 수 없어 CloudFront를 경유해야 하므로 대규모 작업이 필요함 - (D)
Lambda의 동시 실행(Concurrent Executions) 상한을 낮게(예: 5) 제한한다 → 폭주 시 피해를 억제하는 저렴한 보험. 단, 정상적인 이용도 막힐 가능성이 있음 - (E)
Lambda의 reserved_concurrent_executions를 사용하여 탐지 중시. (D)의 경량 버전 - (F) 그 외

다른 MCP와의 정합성을 포함하여, 왜 그런 판단을 내렸는지 한마디 덧붙여 주세요.

📝 나

D의 대응을 취하겠습니다. URL이 유출되지 않는다는 전제의 구성이므로, 여기서는 만약을 위한 방어책으로서 간단히 설정할 수 있는 것을 넣어둡니다.

계정 예약 범위(Account Reservation) 1000을 압박하고 싶지 않으므로, 적지만 3으로 설정합니다. 통상적인 이용이라면 동시 실행 수에 걸리는 일은 없을 것으로 예상합니다.

코멘트도 추가해 주세요.

🎙️ 리뷰어

(Lambda 리소스 정의에 reserved_concurrent_executions = 3을 추가하고, 폭주 대책이라는 취지를 코멘트로 작성하여 commit)

🎙️ 리뷰어

인증의 신뢰 경계 (Trust Boundary) 를 확인합니다.

auth.go:

type TokenValidator struct {
InternalSSOService core_appservice.InternalSSOService
}

즉, 토큰 검증은 InternalSSOService.DecryptAppToken에 위임되어 있습니다. 이는 사내 기반의 "본 MCP용 로그인 토큰"을 복호화하는 메커니즘입니다.

해당 토큰을 가지고 있다는 전제하에, tools_detail.goget_ticket_detail(ticket_id)임의의 ID에 대한 MD 본문을 반환합니다. 즉, 본 MCP용 로그인 토큰을 가진 사람약 600건의 모든 티켓 본문 (새니타이즈(Sanitize) 처리되었다고는 하나)을 읽을 수 있습니다.

질문 (2개, 한꺼번에 답해줘도 OK):

  • 이 본 MCP용 로그인 토큰은 구체적으로 누가 가지고 있다고 가정합니까? (a) 서포트 담당 오퍼레이터 / (b) 사내 엔지니어 전원 / (c) 일부 권한자만 / (d) 엔드 유저 (고객) / (e) 불명
  • "토큰을 가진 전원이 전체 건을 읽을 수 있다"는 권한 모델이 예상 이용 방식과 어긋나지는 않습니까? 새니타이즈 처리가 되었다고는 하나, 예를 들어 "자신의 담당 외 안건을 훔쳐보는 것"이 업무 정책상 허용되는지.

📝 나

c입니다. 토큰을 발행하는 사내 관리 화면에서는 특정 권한을 가진 사용자에게만 발행을 제한하도록 구현되어 있습니다.

예상 이용 방식과 어긋나지 않습니다.

🎙️ 리뷰어

알겠습니다. 토큰 발행이 admin 관리 화면에서 권한 게이트(Gate)되어 있으므로 → MCP 호출 측에서는 그 정도 수준의 액세스 제어는 불필요하다는 신뢰 모델이군요. 실무 예상과 일치합니다.

(보충: searchlog.gouser_token_hash는 SHA256의 앞 8자리 hex입니다. 나중에 "누가 무엇을 보았는지" 조사가 필요해지면, 토큰 ↔ admin user의 발행 기록과 대조하는 것을 전제로 합니다. 그 설계로 문제없어 보입니다)

🎙️ 리뷰어

DB 생성 스크립트로 만든 DB는 여러 서비스 공통의 벡터 검색용 DB입니다. 당초 계획에서는 서비스 전용 DB 명이었으나, 도중에 사용자 지시에 따라 공통 DB로 변경했습니다.

질문: 왜 서비스 전용 DB가 아니라 공통 DB로 했습니까? 다음 관점을 포함하여 설명해 주세요:

  • (a) 향후 확장 시나리오 (타 서비스의 Vector / RAG 용도)
  • (b) TiDB Cloud Starter Plan 측의 사정 (DB 수 및 동일 모델 이용)
  • (c) 테이블 명명 규칙(Naming Convention)에 미치는 영향 (DB가 공통이므로 테이블명은 <서비스명>_tickets / <서비스명>_chunks와 같이 접두사(Prefix)를 붙임. tickets / chunks로 하지 않은 이유)

📝 나

a. 타 서비스의 데이터도 넣을 가능성이 있기 때문에 공통된 명명 규칙을 사용했다. 현시점에서 DB를 나누면 관리가 복잡해지므로, 다중 DB(Multiple DB)는 규모가 커진 후에 검토하면 된다.

b. 이번 아키텍처(Architecture)에서 starter plan 측의 사정은 딱히 관계가 없다. DB 수의 상한도 특별히 없다.

c. 별도 서비스의 데이터를 넣을 때 구분할 수 있도록 접두사(Prefix)로서 서비스명을 붙이고 있다.

🎙️ 리뷰어

타당한 답변입니다.

  • (a) ✓ 「DB를 나누면 관리 복잡화」를 포함하여, 스케일 업(Scale-up) 후에 재고한다는 판단은 옳다.
  • (b) ✓ 저의 당초 표현인 「Starter의 DB 수 제한」은 사실 근거가 약했습니다. DB 수보다는, Cohere의 프레임워크가 DB 단위로 잘라낸 기능이 아니라는 점이 더 정확한 이유입니다. 따라서 「다중 DB로 나눌 의미가 없다」라고 하는 것이 맞습니다.
  • (c) ✓ 서비스명 접두사 (<서비스명>_tickets / <서비스명>_chunks) = 공통 DB의 영역 구분 규칙

🎙️ 리뷰어

후속 페이즈(Phase)에서 처음 적용(Apply)했을 때, Cohere 임베딩(Embedding)이 Malformed input request: required key [input_type] not found 오류로 전멸했습니다. 원인은 DDL 측의 EMBED_TEXT 호출이 2개의 인자 (model, content)만 사용하고, 제3의 인자인 input_type을 전달하지 않았기 때문입니다. 최종적으로:

  • 저장 시 (DDL 파일):
    '{"input_type": "search_document"}'

  • 검색 시 (앱 코드 내의 SQL 리터럴):
    '{"input_type": "search_query"}'

질문: 왜 저장할 때와 검색할 때 input_type의 값을 구분해서 사용해야 하나요? 둘 다

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0