# Altium Pcb Placement

> Altium PCB 배치

- Skill: `scottjeong/altium-pcb-placement` (Agent Skill, multi-file: 17 files)
- Install (CLI): `npx skillmds@latest add scottjeong/altium-pcb-placement`
- Raw SKILL.md: https://api.skillmd.com/api/skills/scottjeong/altium-pcb-placement/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: ScottJeong (https://skillmd.com/u/scottjeong)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/scottjeong/altium-pcb-placement

---


# Altium PCB 배치

배치는 두 종류가 섞여 있다. **섞어서 다루면 둘 다 망한다.**

| | 성격 | 처리 |
|---|---|---|
| **회전·사이즈** | 데이터에서 유도된다. 정답이 하나다 | **계산해서 근거와 함께 제시** |
| **어느 변에 뭘 둘까** | 사용자 취향·기구 제약. 정답이 없다 | **물어보고 그림으로 수렴** |

## 하나만 지킨다면 이것

> **가안은 반드시 그림에서 수렴시킨 다음 Altium 에 넣는다.
> 그림은 싸고 PcbDoc 은 비싸다.**

가안은 **2~4회 바뀐다고 보고 시작한다.** 한 번에 맞지 않는다.
주로 바뀌는 것은 이 넷이고, 전부 §0 에서 미리 물으면 줄일 수 있다.

- 큰 커넥터를 어느 변에 둘지
- 커넥터를 한 변에 몰지, 기능별로 흩을지
- 주요 IC 의 회전 기준을 무엇으로 잡을지
- 보드 크기 목표

**배치 하나하나에 이유를 댈 수 없으면 그건 아직 가안이 아니다.**
"왜 여기?" 에 "딱히" 라고 답할 부품이 있으면 그만큼 보드가 크다.

## 0. 사용자 제약을 먼저 캔다 — 여기서 회차가 갈린다

계산 전에 물어본다. 나중에 알면 전부 다시 그린다.

- **큰 커넥터는 어느 변인가.** 방향이 보드 종횡비를 통째로 결정한다
- **커넥터를 한 변에 몰 것인가**, 기능별로 흩을 것인가
- **결합 상대가 있는가** — 상대 보드가 위로 겹치는지 옆으로 나가는지.
  라이트앵글이면 대개 평면 연장이라 높이 간섭이 없다. **앵글/스트레이트를 확인한다**
- 기구 제약 — 케이스, 고정 홀, 높이 제한
- 보드 크기 목표가 있는가

## 1. 제약 수집

```
python scripts/connectivity_matrix.py <SchDoc> --ref <기준부품>
```

- **부품쌍 넷 수** — 존 분할의 1차 근거. 16넷으로 묶인 두 부품은 붙여 놓는다
- **TP 소속** — 테스트포인트를 어느 부품 옆에 둘지
- **기준부품 핀별 상대** — §2 회전 결정의 입력값

파이썬은 **`altium_monkey` 가 설치된 3.12 venv 인터프리터**를 쓴다
(이 패키지가 Python `<3.13` 을 요구한다). 아래에서는 그걸 `python` 이라고 쓴다.

### 치수는 라이브러리에서 잰다. PCB 는 필요 없다

```
python scripts/measure_from_lib.py <SchDoc> --libs <라이브러리폴더>
```

회로도가 참조하는 풋프린트 이름을 뽑아 `.PcbLib` 에서 bbox 를 잰다.
**PcbDoc 도, Update PCB 도 필요 없다.** 외주 PCB 설계업체에 넘길 가안처럼
애초에 PCB 문서가 없는 경우가 오히려 흔하다.

| 출처 | 얻는 것 | 비고 |
|---|---|---|
| **라이브러리 `.PcbLib`** | **풋프린트 bbox·패드·홀 — 원본** | 기본 경로 |
| PcbDoc (있으면) | 같은 값 + 현재 배치·회전 | **복사본이다** (아래) |
| 벤더 도면 PDF | 몸체, 레버 스윙, 높이, 볼트홀 — 풋프린트에 없는 것 | altium-library `measure_drawing.py` |

**PcbDoc 은 풋프린트를 복사해 들고 있다.** 라이브러리를 고쳐도
`Tools ▸ Update From PCB Libraries` 전까지 PcbDoc 은 옛 모양 그대로다.
실측 17건 중 2건이 이것 때문에 0.2~0.5mm 어긋났다. **라이브러리가 원본이다.**

### 라이브러리가 없으면 데이터시트로 간다 — 추정으로 가지 않는다

**PCB 외주 설계업체를 쓸 때는 풋프린트 라이브러리를 아예 안 만드는 게 흔하다.**
그 경우가 배치 가안이 가장 필요한 경우이기도 하다. 라이브러리 없음은 정상 상황이지
포기 사유가 아니다.

부품마다 이 사다리를 위에서부터 내려간다. **맨 아래로 떨어지지 마라.**

| # | 출처 | 얻는 것 | 표시 |
|---|---|---|---|
| 1 | 라이브러리 `.PcbLib` | 실제 랜드+실크 | 실측 |
| 2 | 데이터시트 **권장 랜드패턴 / PCB layout 뷰** | 랜드 실치수 → 코트야드만 더함 | 실측 |
| 3 | 데이터시트 **몸체 치수표** (D/E/e/b/L) | 랜드를 유도 → 코트야드 | **유도값** |
| 4 | 패키지 이름만 (`QFN32 4x4`) | IPC-7351 표준 랜드 | **유도값** |
| 5 | 추정 | — | **금지** |

라이브러리 경로 찾는 순서: 프로젝트 폴더 → Altium 워크스페이스
(`%APPDATA%\Altium\Altium Designer {GUID}\LastWorkspace\*.DsnWrk` 안에 `.LibPkg`
절대경로가 그대로 있다) → **사용자에게 묻는다.** 전체 드라이브 `find` 는 타임아웃 난다.

### 몸체 치수를 그대로 쓰지 마라

데이터시트 치수표의 `D`/`E` 는 **몸체**다. 보드가 먹는 면적이 아니다.
랜드패턴이 몸체 밖으로 나오고, 그 위에 코트야드와 실크가 얹힌다.

실측 대조 — 몸체 대비 실제 풋프린트가 얼마나 커지는가:

| 부품 | 몸체 | 라이브러리 풋프린트 |
|---|---|---|
| QFN32 | 4.00 × 4.00 | **7.05 × 7.00** |
| SOT-223 | 6.50 × 3.50 | **8.83 × 7.25** |
| 0805 | 2.00 × 1.25 | **3.90 × 1.90** |
| 0603 | 1.60 × 0.80 | **2.30 × 1.10** |

**일정한 배수가 없다.** 라이브러리·업체마다 실크 여유가 달라서 단일 계수 환산은 틀린다.
그러니 3~4단계로 잡은 값은:

- 랜드패턴을 먼저 유도하고, 그 위에 코트야드를 더한다 (유도 규칙은 IPC-7351 확인)
- 가안 `label` 에 **「유도값」** 이라고 적는다
- **배치 여유를 더 준다.** 나중에 실물 풋프린트가 크면 다시 그려야 한다

공칭·기억으로 잡은 값이 실측과 얼마나 벌어지는지 (실측 대조):

| 부품 | 공칭으로 잡기 쉬운 값 | 실제 풋프린트 |
|---|---|---|
| QFN32 | 4 × 4 (몸체/다이 크기) | **7.05 × 7.00** — 43% 과소 |
| DC 잭 | 14 × 11 | **9.90 × 14.40** — 폭·깊이가 뒤집힌다 |
| DIP 스위치 4연 | 10 × 6 | **12.34 × 10.90** |

**크기보다 나쁜 건 방향이다.** 실측을 미루면 핀이 어느 쪽으로 나오는지까지
기억으로 추론하게 되고, 그건 재보면 반대인 경우가 있다. 30초짜리 측정이다.

벤더 도면 실측이 정확했다고 나머지도 괜찮을 거라 넘기지 마라 — 그게 이 사고의 경로였다.

몸체와 금지구역은 다르다. 테스트 소켓은 **레버가 도는 범위**가 몸체보다 크고,
그 밑에는 테스트포인트를 못 깐다(프로브가 안 들어간다). 실측 예: 몸체 36×36,
레버 포함 46.2, 높이 31.7 — 이 세 값이 전부 다른 제약이다.

## 2. 회전 결정 — 이 스킬의 핵심

**QFN/QFP 는 핀번호가 변을 결정하므로 회전이 순수 계산으로 나온다.**

```
python scripts/pin_side_map.py --pins-per-side 16 --start left --dir ccw \
  --group "MII=1-12,61-68" --group "BUS=14-22,25,32,33" \
  --want "MII=NE" --want "BUS=W"
```

`--start` / `--dir` 는 **본드 도면이나 패키지 도면으로 확인해서 넣는다.**
추정하면 결과가 통째로 뒤집힌다. 자세한 절차와 실증 예는 `references/rotation-decision.md`.

출력은 회전 0/90/180/270 각각에서 각 신호 무리가 어느 방향으로 나오는지다.
**코너(NE/NW/SE/SW)로 나온 무리는 그 대각에 상대 부품을 놓으면 교차가 0 이 된다.**

**커넥터는 이 방법으로 안 나온다.** 커넥터에 필요한 건 핀 매핑이 아니라
**개구부가 어디를 보는가** 다. 「패드 bbox 대비 실크가 더 튀어나온 쪽」이 개구부고,
그 방향이 보드 바깥을 향해야 한다.

```
python scripts/connector_facing.py <SchDoc> --libs <라이브러리폴더>
```

**bbox 는 180° 회전으로 안 변하므로 겹침·외곽 검사에 안 걸린다.**
배치를 끝낸 뒤 이 표와 실제 회전을 따로 대조한다. 안 하면 개구부가 보드 안쪽을
향한 채로 넘어간다.

이건 사용자에게 물어볼 일이 아니라 **계산 결과를 근거와 함께 제시할 일**이다.
비교표를 그대로 보여준다 — 왜 다른 3개가 아닌지가 같이 보여야 납득된다.

## 3. 보드 사이즈

**축별로 치수를 누적해서 역산한다.** 어림하지 않는다.

```
X: 커넥터 온보드 깊이 + 라우팅 채널 + 소켓존(금지구역+TP뱅크) + 주변IC + 여유
Y: (그 축을 지배하는 부품 하나) + 양끝 여유
```

한 축은 대개 **부품 하나가 강제**한다(긴 커넥터 등). 그 축은 못 줄인다.
다른 축이 자유롭다 — 거기서 낭비를 찾는다. 자세한 건 `references/board-sizing.md`.

**모든 배치에 이유를 대본다.** "왜 여기?" 에 답이 "없다" 인 부품이 하나라도 있으면
그만큼 보드가 크다. 커넥터 하나를 이유 없이 대각선에 두면 그 축이 30mm 커진다.

## 4. 가안 도면 — 반복하는 단계

```
python scripts/plan_svg.py plan.json -o <출력경로>
```

`plan.json` 스키마는 `references/plan-schema.md`,
**동작하는 예제는 `examples/plan.example.json`** — 거기서 시작하는 게 빠르다.

- **반드시 1:1 축척.** 눈대중은 "들어갈 것 같다" 를 만들고 그건 늘 틀린다
- 금지구역·테스트포인트 뱅크·주요 신호 흐름·NOTE 를 같이 그린다
- **NOTE 에 판단 근거를 적는다.** 나중에 "왜 이렇게 했지" 를 푸는 유일한 기록이다
- 생성 후 **PNG 를 직접 읽어서 겹침을 확인한다.** 라벨이 부품을 가리거나 부품이
  홀과 겹치는 건 코드로 안 잡히고 그림을 봐야 보인다

사용자 피드백이 오면 JSON 만 고쳐 다시 렌더한다. **이 왕복이 이 스킬의 정상 동작이다.**
2~4회는 당연하다고 보고 진행한다.

### 테스트포인트 뱅크

- 피치를 먼저 정하고 span 을 계산한다. **뱅크 span 이 부품 몸체보다 크면 안 된다**
- 금지구역이 있는 변은 몸체가 아니라 **금지구역 바깥**에 붙인다
- 실제 패드 지름을 모르면 그렇게 적는다. 피치가 패드보다 작으면 다시 그려야 한다

## 5. 겹침 검사

배치를 넣은 뒤(또는 넣기 전 시뮬레이션으로) 물리 충돌을 본다.

```
python scripts/overlap_check.py <PcbDoc> --pads-only --gap 0.3
```

`--pads-only` 는 실크를 빼고 패드만으로 본다 (실크 겹침은 무해할 때가 많다).
`--gap` 은 요구 최소 간격 mm — 0 이면 실제 교차만 잡는다.

출력은 네 갈래이고 **심각도가 다르다.**

| 항목 | 뜻 |
|---|---|
| 같은 면 겹침 | 진짜 충돌. 고친다 |
| 반대면 관통홀 충돌 | THT 핀·기구홀이 반대면 부품을 뚫는다. 고친다 |
| 패드가 보드 밖 | 제조 불가. 고친다 |
| 실크·하우징만 보드 밖 | 엣지 커넥터면 정상. 눈으로만 확인 |

### 반대면끼리 XY 가 겹치는 것은 겹침이 아니다

디커플링은 **반대면에 놓는 게 정석**이다. 소켓·대형 IC 는 몸체가 주변을 덮어서
같은 면에서는 핀 가까이 못 간다. 실측 예 — 36mm 소켓에서 같은 면 최근접이
핀에서 15.5mm, 반대면으로 내리면 2.3mm 였다.

그래서 **레이어를 안 보는 겹침 검사는 이 배치에서 통째로 오탐이 된다.**
`overlap_check.py` 는 면을 갈라 판정한다.

단 관통홀은 예외다. 홀은 판을 뚫으므로 반대면 부품과 진짜로 부딪친다.
**관통홀은 하나씩 비교한다** — 네 모서리에 기구홀이 있는 소켓은 홀들을 합친
bbox 가 몸체 전체가 되어, 그 아래 반대면 부품을 전부 오탐으로 잡는다.

**이 검사는 커넥터 개구부 방향을 못 잡는다.** bbox 가 180° 회전으로 안 변하기
때문이다 — §2 의 `connector_facing.py` 로 따로 대조한다.

## 6. 좌표 투입 — 승인 후 1회

**사용자가 "이대로 확정" 이라고 말하기 전에는 하지 않는다.**

```
python scripts/plan_to_placements.py plan.json > placements.json
```
→ `altium-mcp` **`place_components`** 에 그대로 넣는다 (배치 전용 배치 API, 1 undo 단계).

**단위·기준이 다르다. 그대로 넣으면 어긋난다.**

| 가안 | Altium |
|---|---|
| mm | **mils** |
| bbox 좌하단 | **컴포넌트 원점** (풋프린트에 따라 pad1 이거나 몸체 중심) |
| — | rotation 은 **반시계(CCW) 도** |

그래서 **2패스**다. 1패스로 넣고, 실제 bbox 를 읽어 의도한 중심과의 차를 구해
`--offsets` 로 2패스를 돌린다. 함정은 `references/injection.md`.

`place_components` 는 반환값에 Altium 이 보고하는 최종 x/y/rotation 이 들어 있다.
**그걸 읽어서 대조한다.** 넣고 끝내면 어긋난 걸 모른다.

## 어디까지 자동으로 할 것인가 — 이게 이 스킬의 경계다

| | 자동 | 이유 |
|---|---|---|
| 보드 사이즈·외곽·고정홀 | **○** | 치수 누적. 정답이 하나 |
| 주요 IC 회전 | **○** | 핀→변 매핑에서 유도된다 |
| 큰 부품(커넥터·소켓·IC) 배치 | **○** | 변 제약·상대 거리로 풀린다 |
| 테스트포인트 뱅크 | **○** | 핀 순서대로 규칙 격자 |
| 겹침·외곽밖 검사 | **○** | 순수 기하 |
| **패시브 배치** | **✕ 사람이 한다** | 아래 |
| 라우팅·자동배선 | ✕ | MII·차동쌍은 손으로 |
| DRC 판정 / 넷클래스·룰 | ✕ | 별개 작업 |

### 패시브를 자동으로 놓지 마라

패시브 자리는 **라우팅 의도**가 정한다 — 디커플링 루프 면적, GND 리턴 경로,
트레이스가 IC 어느 변으로 빠지는지, 어느 패드가 비아로 내려가는지.
규칙이 아는 건 "누구와 넷을 공유하나" 와 "안 겹치나" 둘뿐이라 그 위 정보가 없다.

**결과: 겹치지 않는 배치는 만들어도 좋은 배치는 못 만든다.**

규칙을 네 번 고쳐 겹침 0 까지 만들어도 결론은 같다 —
**큰 부품은 쓸 만하고 패시브는 수동이 낫다.**

**그러니 패시브는 「주인 IC 근처 · 겹침 0」 까지만 만들고 넘긴다.**
그 상태가 손으로 다듬기 좋은 출발점이다. 규칙을 더 정교하게 만들려 하지 마라 —
그 방향으로는 답이 안 나온다.

## 참고 파일

| 파일 | 언제 |
|---|---|
| `references/rotation-decision.md` | §2 회전. 핀→변 매핑 확인법과 실증 예 |
| `references/board-sizing.md` | §3 사이즈. 축 누적법 |
| `references/plan-schema.md` | §4 plan.json 스키마 |
| `references/injection.md` | §6 투입 **전에**. 단위·원점 함정 |

| 스크립트 | 용도 |
|---|---|
| `scripts/apply_outline.py` | 보드 외곽(모서리 라운드) + 대칭 고정홀. **Altium 이 문서를 닫은 상태에서** |
| `scripts/measure_from_lib.py` | §1 회로도+라이브러리만으로 풋프린트 실측 |
| `scripts/connector_facing.py` | 커넥터 개구부 방향 → 변별 회전값. **배치 후 반드시 대조** |
| `scripts/connectivity_matrix.py` | §1 부품쌍 넷 수 · TP 소속 · 핀별 상대 |
| `scripts/pin_side_map.py` | §2 회전 4안 비교 |
| `scripts/plan_svg.py` | §4 가안 도면 |
| `scripts/plan_to_placements.py` | §6 mm/bbox → mils/원점 변환 |

관련: 회로도 검토 **`altium-schematic-review`** · 라이브러리 제작 **`altium-library`**.

