기술 설명서 작성 규칙
구현 세부사항 나열을 막고 읽는 사람이 무엇을 할 수 있게 되는지 기준으로 쓰게 하는 규칙.
내 환경에 맞게 만들기
무엇을 하는 규칙인가
이 규칙을 쓰는 이유
기술 설명서를 AI에게 맡기면 대개 **내부 구현**이 먼저 나온다. "이 모듈은 큐를 사용하며 워커가 폴링 방식으로 처리한다" 같은 설명은 만든 사람에게는 자연스럽지만, 처음 쓰는 사람에게는 아무 쓸모가 없다. 읽는 사람이 알고 싶은 것은 세 가지다. 이걸로 무엇을 할 수 있는지, 어떻게 시작하는지, 무엇이 안 되는지. 아래 규칙은 그 세 가지가 빠지지 않게 만드는 것이 목적이다.
섹션 순서
이 순서를 바꾸지 않는다. 1. **이 문서로 할 수 있는 것** 2. **시작하는 법** (전제조건 → 단계) 3. **안 되는 것 / 제약** 내부 구현 설명이 필요하면 부록으로 분리하고, 본문 세 섹션 앞에 두지 않는다.
기능이 아니라 행동 결과로 쓴다
"~을 지원한다", "~기능이 있다"로 끝내지 않는다. **그 기능을 쓰면 실제로 무엇이 달라지는지**를 쓴다. - 이렇게 쓰지 않는다: `이 API는 캐시를 지원한다` - 이렇게 쓴다: `같은 요청은 5분 안에 다시 오면 두 번째부터 즉시 응답한다. 5분이 지나면 다시 원본 데이터를 조회한다.` 두 번째 문장이 있어야 읽는 사람이 자기 상황에 맞는지 판단할 수 있다.
단계는 전제조건부터, 실행 가능한 순서로 쓴다
전제조건을 생략하고 명령만 나열하지 않는다. **무엇이 준비돼 있어야 하는지**를 먼저 쓴다. - 이렇게 쓰지 않는다: `설정 파일을 수정한다` - 이렇게 쓴다: `config.yaml의 timeout 값을 30 이상으로 바꾼다(기본값 10초는 대용량 요청에서 타임아웃이 난다). 값을 바꾼 뒤에는 프로세스를 재시작해야 반영된다.` 값을 왜 바꾸는지와, 바꾼 뒤 무엇을 더 해야 하는지가 함께 있어야 재현된다.
제약과 한계는 숨기지 않는다
"모든 경우에 동작한다" 식으로 뭉뚱그리지 않는다. **어떤 조건에서 안 되는지**를 구체적으로 쓴다. - 이렇게 쓰지 않는다: `대부분의 경우 안정적으로 동작합니다` - 이렇게 쓴다: `동시 요청이 초당 100건을 넘으면 큐가 밀리기 시작한다. 그 이상 트래픽이 예상되면 별도 문의가 필요하다.` 제약을 먼저 알려주는 쪽이, 나중에 장애로 알게 되는 쪽보다 신뢰를 덜 깎는다.
분량과 예시 코드
- 코드 예시는 **그대로 실행해서 재현되는 것만** 넣는다. 일부만 발췌해 실행 안 되는 예시는 넣지 않는다. - 알아야만 이해되는 배경지식이 있으면 앞에 한 줄로 전제한다("이 문서는 OO를 이미 설치했다고 가정한다"). - 평서체로 쓴다. 과장하는 수식어("강력한", "완벽하게")를 붙이지 않는다.
쓰기 전에 확인할 것
- 1·2·3번 섹션이 모두 있는가 - 기능 설명이 아니라 행동 결과로 쓰였는가 - 단계에 전제조건이 빠져 있지 않은가 - 제약·한계가 명시돼 있는가 - 코드 예시가 실제로 실행되는가 흔히 나오는 실패 예시는 `bad-examples.md`에 정리해 두었다. 처음 몇 번은 그것과 비교해 보면 빠르다.
원문 전문
넣기 전에 전부 읽어보실 수 있습니다. 이 파일은 AI에게 지시를 주는 문서입니다. 내용을 확인하고 넣으세요.
item.md
## 이 규칙을 쓰는 이유
기술 설명서를 AI에게 맡기면 대개 **내부 구현**이 먼저 나온다. "이 모듈은 큐를 사용하며 워커가 폴링 방식으로 처리한다" 같은 설명은 만든 사람에게는 자연스럽지만, 처음 쓰는 사람에게는 아무 쓸모가 없다. 읽는 사람이 알고 싶은 것은 세 가지다. 이걸로 무엇을 할 수 있는지, 어떻게 시작하는지, 무엇이 안 되는지.
아래 규칙은 그 세 가지가 빠지지 않게 만드는 것이 목적이다.
## 섹션 순서
이 순서를 바꾸지 않는다.
1. **이 문서로 할 수 있는 것**
2. **시작하는 법** (전제조건 → 단계)
3. **안 되는 것 / 제약**
내부 구현 설명이 필요하면 부록으로 분리하고, 본문 세 섹션 앞에 두지 않는다.
## 기능이 아니라 행동 결과로 쓴다
"~을 지원한다", "~기능이 있다"로 끝내지 않는다. **그 기능을 쓰면 실제로 무엇이 달라지는지**를 쓴다.
- 이렇게 쓰지 않는다: `이 API는 캐시를 지원한다`
- 이렇게 쓴다: `같은 요청은 5분 안에 다시 오면 두 번째부터 즉시 응답한다. 5분이 지나면 다시 원본 데이터를 조회한다.`
두 번째 문장이 있어야 읽는 사람이 자기 상황에 맞는지 판단할 수 있다.
## 단계는 전제조건부터, 실행 가능한 순서로 쓴다
전제조건을 생략하고 명령만 나열하지 않는다. **무엇이 준비돼 있어야 하는지**를 먼저 쓴다.
- 이렇게 쓰지 않는다: `설정 파일을 수정한다`
- 이렇게 쓴다: `config.yaml의 timeout 값을 30 이상으로 바꾼다(기본값 10초는 대용량 요청에서 타임아웃이 난다). 값을 바꾼 뒤에는 프로세스를 재시작해야 반영된다.`
값을 왜 바꾸는지와, 바꾼 뒤 무엇을 더 해야 하는지가 함께 있어야 재현된다.
## 제약과 한계는 숨기지 않는다
"모든 경우에 동작한다" 식으로 뭉뚱그리지 않는다. **어떤 조건에서 안 되는지**를 구체적으로 쓴다.
- 이렇게 쓰지 않는다: `대부분의 경우 안정적으로 동작합니다`
- 이렇게 쓴다: `동시 요청이 초당 100건을 넘으면 큐가 밀리기 시작한다. 그 이상 트래픽이 예상되면 별도 문의가 필요하다.`
제약을 먼저 알려주는 쪽이, 나중에 장애로 알게 되는 쪽보다 신뢰를 덜 깎는다.
## 분량과 예시 코드
- 코드 예시는 **그대로 실행해서 재현되는 것만** 넣는다. 일부만 발췌해 실행 안 되는 예시는 넣지 않는다.
- 알아야만 이해되는 배경지식이 있으면 앞에 한 줄로 전제한다("이 문서는 OO를 이미 설치했다고 가정한다").
- 평서체로 쓴다. 과장하는 수식어("강력한", "완벽하게")를 붙이지 않는다.
## 쓰기 전에 확인할 것
- 1·2·3번 섹션이 모두 있는가
- 기능 설명이 아니라 행동 결과로 쓰였는가
- 단계에 전제조건이 빠져 있지 않은가
- 제약·한계가 명시돼 있는가
- 코드 예시가 실제로 실행되는가
흔히 나오는 실패 예시는 `bad-examples.md`에 정리해 두었다. 처음 몇 번은 그것과 비교해 보면 빠르다.
references/bad-examples.md
# 흔한 실패 예시와 교정
기술 설명서에서 반복적으로 나오는 네 가지 패턴이다. 왼쪽이 자주 나오는 문장, 오른쪽이 교정이다.
## 1. 구현 세부사항부터 시작
**자주 나오는 것**
> 이 시스템은 메시지 큐를 통해 비동기로 이벤트를 처리하며, 워커 프로세스가 큐를 폴링하는 구조로 되어 있습니다.
만든 사람의 관점이다. 처음 읽는 사람은 이걸로 뭘 할 수 있는지 아직 모른다.
**교정**
> 이 시스템을 쓰면 알림 발송 요청을 보낸 즉시 응답을 받고, 실제 발송은 백그라운드에서 처리된다. 발송 실패 시 최대 3번 재시도한다.
무엇이 가능해지는지를 먼저 쓰고, 내부 구조는 필요하면 뒤에 붙인다.
## 2. 전제조건 없는 단계
**자주 나오는 것**
> 1. 환경변수를 설정한다
> 2. 서버를 실행한다
어떤 환경변수를, 어떤 값으로 설정해야 하는지 없으면 따라 할 수 없다.
**교정**
> 1. `.env`에 `API_KEY`를 발급받은 키로 설정한다. 키는 관리 콘솔의 "API 키" 메뉴에서 발급한다.
> 2. `npm start`로 서버를 실행한다. 기본 포트는 3000이며, `PORT` 환경변수로 바꿀 수 있다.
값을 어디서 구하는지, 기본값이 무엇인지까지 있어야 재현된다.
## 3. 제약을 숨기는 과장
**자주 나오는 것**
> 대용량 트래픽에서도 안정적으로 동작하도록 설계되었습니다.
"안정적으로"는 판단이지 정보가 아니다. 어느 수준까지 안정적인지 알 수 없다.
**교정**
> 초당 100건까지는 응답 지연이 100ms 이하로 유지된다. 그 이상에서는 큐 적체로 지연이 늘어나며, 별도 스케일 설정이 필요하다.
숫자 기준을 명시하면 읽는 사람이 자기 트래픽에 맞는지 스스로 판단할 수 있다.
## 4. 실행되지 않는 코드 예시
**자주 나오는 것**
> ```
> client.request(...)
> // 나머지 설정은 생략
> ```
일부만 보여주고 나머지는 "생략"이라고 하면 그대로 실행해도 동작하지 않는다.
**교정**
> ```
> const client = new ApiClient({ apiKey: process.env.API_KEY });
> const result = await client.request({ path: "/users", method: "GET" });
> ```
임포트부터 실행까지 그대로 복사해 돌려볼 수 있는 형태로 남긴다.
## 확인 순서
작성 후 위에서부터 훑으며 네 가지를 본다.
1. 구현 설명이 사용법보다 먼저 나오는가
2. 전제조건 없는 단계가 있는가
3. 근거 없는 안정성 수식어가 있는가
4. 그대로 실행되지 않는 코드 예시가 있는가
하나라도 걸리면 그 문장만 고친다. 전체를 다시 쓸 필요는 없다.