4편에서는 만든 앱을 라즈베리파이에 올려 돌리는 것까지 다뤘다. 그리고 그 편 끝에, 그 예제의 래더도 실은 AI를 시켜 짠 것이라는 한 줄을 붙여 뒀다. 이번 편은 그걸 어떻게 시켰는지, AI 워크플로우 이야기다.

1편에 이런 생각을 적었다. 코드를 이렇게 잘 짜는데 래더라고 못 할까. 래더도 결국 로직이다. 이번에 그걸 실제로 해봤다.

먼저 인정하고 시작한다. AI가 래더를 짠다는 말에 PLC 하는 사람은 손이 오그라든다. 나도 그랬다. 코드는 버그가 나면 고치면 되지만 래더는 모터가 돌고 실린더가 움직인다. 잘못 짜면 사람이 다친다. 그 거부감을 모르고 하는 얘기가 아니라는 걸 먼저 적어 둔다.

예제는 교차로 신호등으로 잡았다. 남북·동서 두 방향에 직진과 좌회전 전용 신호, 보행 신호와 보행 요구 버튼, 자동·야간 점멸·수동 세 가지 운전 모드, 비상차량 우선까지. 그리고 안전 요구가 하나 붙는다. 상충하는 방향이 동시에 초록이 되면 절대 안 된다. 누구나 아는 교과서 예제지만 얽힌 조건은 만만치 않다.

이걸 AI한테 시켰고 됐다. 이번 편은 그 “됐다”를 뜯는 기록이다.

AI Mode와 Assistant Mode

프로젝트를 시작하는 모드가 둘 있다. AI 모드는 AI가 인터뷰부터 검증까지 창작을 끌고 간다. 어시스턴트 모드는 그 창작을 건너뛰고 곧장 편집으로 들어간다. 프로젝트를 새로 만들고 AI 대화를 열면 이 둘 중 무엇으로 시작할지 먼저 묻는다.

프로젝트 시작 시 AI 모드와 어시스턴트 모드를 고르는 선택 화면

어시스턴트 모드를 고르면 아무 일도 일어나지 않는다. 빈 프로젝트가 열리고 3편에서 한 것처럼 심볼을 등록하고 래더를 그린다. AI 없이 쓰는 Senbrix다. 현장에는 AI를 반기지 않는 사람이 더 많다는 걸 안다. 그 사람들한테 이 도구가 AI 없이는 못 쓰는 물건이면 안 된다. 그렇다고 이 모드가 AI와의 절연은 아니다. 이렇게 시작한 프로젝트에서도 필요하면 래더 수정이나 검토 같은 일을 AI에게 시킬 수 있다. 그래서 이 모드의 이름이 어시스턴트다.

AI 모드를 고르면 이번 편의 흐름이 시작된다. 다만 이 모드는 프로젝트를 만드는 동안만 산다. 검증이 끝나면 자동으로 어시스턴트 모드로 전환되고 그 뒤로는 그냥 시작한 프로젝트와 똑같아진다. AI가 계속 주도권을 쥐는 구조가 아니다.

이 갈림길을 먼저 적어 둔다. 이 시스템에서 AI는 골라 쓰는 쪽이지 필수가 아니다. 안 고르면 평범한 PLC 개발 환경이고 고르면 지금부터 하는 이야기가 된다.

어떻게 시키나

AI 모드는 다섯 단계로 프로그램을 만든다. 그런데 다섯이 다 같은 성격은 아니다. 앞의 셋은 사람이 함께 짚고 승인하는 단계, 뒤의 둘은 승인된 설계를 AI가 알아서 닫는 단계다.

인터뷰 → 설계 → 계획   ← 사람이 개입하고 승인한다
구현 → 검증           ← AI가 닫는다

“신호등 만들어줘” 한 마디에 래더가 쏟아지는 구조가 아니다. 인터뷰로 요구사항을 닫고 설계 문서를 쓴다. 구현 계획까지 세운 다음에야 래더를 짜고 다 짜면 검증한다.

단계 사이에는 문턱이 있다. 설계와 계획은 문서를 다 쓰면 승인 대기 상태로 멈춘다. 사람이 검토하고 승인해야 다음으로 간다. 대화 중에 “진행해” 같은 말을 흘려도 그걸 남은 단계 전부에 대한 승인으로 넘겨짚지 않는다. 지금 걸려 있는 문턱 하나만 통과시킨다. 우회로는 없다.

진행 상태는 파일에 남는다. AI의 기억에 맡기지 않는다. 프로젝트 폴더의 progress.json에 지금 어느 단계고 무엇이 끝났고 무엇이 남았는지가 적힌다. 대화 세션이 끊겨도 며칠 뒤에 다시 열어도, AI는 이 파일을 읽고 같은 자리에서 이어간다. 이 파일이 있어서 재개할 때 처음부터 다시 묻지 않는다. 에디터의 AI 페이지는 이 파일을 진행표로 보여주고 사람은 여기서 어디까지 왔는지 본다.

신호등은 인터뷰부터 시작했다. 내가 만들 것을 설명하자 AI가 먼저 교차로 신호 제어의 구성 요소를 정리해 왔다. 그리고 갈리는 지점마다 선택지를 만들어 물었다. 굵은 질문들만 추리면 이렇다.

카테고리물은 것내 답
동작 순서4현시를 어떤 순서로 돌릴까. 선행 좌회전(추천)·후행 좌회전·동시 현시선행 좌회전
설비 구성프로그램을 어떻게 나눌까. 공통·현시 시퀀서·남북 출력·동서 출력·안전 인터록, 다섯 블록 제안그대로 확정
운전 방식자동·야간·수동 전환을 무엇으로 할까. 제어반 셀렉터(추천)·시간대 자동 전환·버튼 순환셀렉터 스위치
시간 수치황색·전적색·보행 시간을 얼마로 잡을까편람 표준값으로 확정
안전·알람상충 금지·최소 황색에 더해, 고장이 감지되면 어느 쪽으로 떨어질까전방향 적색 점멸, 사람이 복구할 때까지

추천에는 근거가 붙어 온다. 선행 좌회전이면 국내 표준에 가깝고 좌회전 대기 차량을 처리하기 좋다는 설명이 달려 있었다. 나는 그대로 골랐다. 답은 대부분 이렇게 카드에서 하나 고르는 클릭이었다.

골격이 잡히면 블록마다 파고드는 질문이 이어진다. 블록 하나마다 채워야 할 칸이 정해져 있다. 언제 동작해도 되는지(허가), 무엇이 동작을 시키는지(명령), 무엇을 내보내는지(출력), 지금 어느 상태인지(상태), 고장을 어떻게 알아채는지(고장), 다른 블록과 어떻게 주고받는지(연계). 이 칸이 다 채워져야 그 블록이 닫힌다. 아래 진행표 캡처를 보면 블록마다 그 칸이 그대로 나와 있다.

수치도 지어내지 않았다. 황색 시간을 정할 때 AI가 경찰청 교통신호기 설치·운영 업무편람을 검색해 왔다. 횡단 폭 2025m에 시속 50km면 황색 3초, 3040m면 4초, 45m를 넘으면 5초. 전적색은 1~2초. 보행 최소 녹색은 3.2초에 횡단 길이를 보행 속도로 나눈 값을 더한다. 이 표준값을 근거로 제안했고 나는 확인만 했다.

인터뷰 단계 진행표 - 블록마다 허가·명령·출력·상태·고장·연계 칸이 채워진 모습

인터뷰가 닫히면 AI가 설계 문서를 쓰고 승인 대기로 멈춘다. 상태기계와 등화 표와 안전 가드가 담긴 design.md 한 벌이다. 사람은 이 문서를 읽고 승인하는 자리에 앉는다. 나는 도장만 찍고 넘기지 않았는데 그 얘기는 걸러진 것들 절에서 한다.

설계가 승인되면 계획이다. 설계를 구현 단위로 쪼개서 무엇을 어떤 순서로 짤지, 각각 무엇으로 검증할지를 적고 또 멈춘다. 두 번의 멈춤을 지나야 AI가 래더에 손을 댄다. 오간 문서는 대화 속에 흩어지지 않는다. 설계는 design.md, 계획은 plan.md 파일로 프로젝트에 남는다. 구현과 검증은 앞서 말한 대로 AI가 닫고 검증까지 끝나면 AI 모드가 내려가면서 프로젝트는 어시스턴트 상태가 된다.

이 흐름에서 내가 보낸 승인은 각각 두 글자였다. 설계 승인이 “승인”, 계획 승인이 “진행”. 짧지만 이 두 마디가 없으면 워크플로우는 그 자리에 멈춰 서 있다.

여기까지가 겉에서 본 절차다. 그래서 뭐가 나왔나.

뭐가 나왔나

산출물부터 늘어놓는다. 래더 85줄, 심볼 59개, 기능 블록 다섯, IO-8 보드 4장, 설계 문서와 구현 계획 문서 각 한 벌. C#은 한 줄도 안 썼다. 신호등은 조건과 타이머와 상태 전이뿐이라 래더만으로 충분하다고 AI가 판단했고 App.cs를 비워 뒀다. 3편에서 래더와 C#의 역할을 나눴는데 이번 예제는 C#의 일이 없는 쪽이었다. 계산이 필요 없는 곳에 코드를 안 쓰는 것도 판단이다.

설계 문서

설계 문서를 통째로 옮기면 지루하니 조각만 꺼낸다. 우선 운전 모드다. 셀렉터 세 입력을 매 사이클 판정한다.

자동·야간·수동 중 정확히 하나만 켜져 있어야 한다. 0개면 단선, 2개 이상이면 오배선이다. 어느 쪽이든 고장으로 보고 전방향 적색 점멸로 떨어진다.

로터리 셀렉터가 물리적으로 한 자리만 켜는 물건이라는 것까지 설계에 반영돼 있다. 접점이 하나도 안 켜진 순간을 단선으로 의심한다. 현장에서 배선 뽑혀 본 사람이 쓸 법한 방어다.

다음은 시퀀서. 4현시 순환의 골격이 상태 전이도로 나왔다.

[INIT] 전적색 2초

[P1G]─황요구→[P1Y]─3초→[P1AR]─분기→[P2G]─황요구→[P2Y]─3초→[P2AR]─분기→
 남북좌 녹      남북좌 황    전적색     남북직진 녹     황        전적색
→[P3G] 동서좌 … →[P4G] 동서직진 … →[P4AR]─분기→(P1G로 복귀)

황요구 = (자동 이고 현시 타이머 만료)
       또는 (수동 이고 진행 버튼 상승)
       또는 (비상 우선 이고 최소 녹색 5초 경과)

초록에서 황으로 넘어가는 조건이 하나로 묶여 있다. 자동은 타이머가, 수동은 버튼이, 비상 우선은 요청이 같은 전이를 당긴다. 그리고 어느 경로로 가든 황색 3초와 전적색 2초는 건너뛸 수 없다. 상태기계 구조상 녹에서 적으로 바로 가는 길이 없다. 최소 황색 보장을 감시 로직으로 덧대지 않고 길 자체를 그렇게 냈다.

시간 값은 인터뷰에서 편람 기준으로 확정한 것들이 그대로 타이머 설정이 됐다.

자리시간
좌회전 녹15초
직진 녹30초
황색3초
전적색2초
비상 우선 최소 녹색5초
보행 녹 고정 점등 / 점멸15초 / 10초

비상차량 우선도 상태로 풀었다. 요청이 들어와도 즉시 끼어들지 않는다. 지금 현시를 황색과 전적색으로 정상적으로 끝낸 뒤에 해당 방향 직진 녹색으로 넘어가 잡아 둔다. 요청이 풀리면 그 직진의 황색으로 합류해서 원래 순환으로 돌아온다. 모드 전환도 마찬가지로 전적색에서만 받는다. 어떤 전환이든 모든 방향이 적색인 순간을 거쳐서 간다.

등화 출력은 상태만 보고 켠다. 남북 직진 녹은 지금 상태가 P2G일 때 켜진다. 전이 조건은 보지 않는다. 이렇게 해 두면 나중에 정비하다가 이 등이 왜 켜졌냐고 물었을 때 답이 항상 하나다. 지금 이 상태라서.

보행은 요구가 있을 때만 처리한다. 버튼은 순간 접점이라 눌린 걸 자기유지로 잡아 두고 해당 직진 현시가 끝나는 전적색에서 푼다. 요구가 없으면 보행등은 계속 적색이고 차량 녹색은 그대로 간다. 보행 녹색은 15초 고정 점등 뒤 10초 점멸로 끝낸다. 편람에서 가져온 그 공식이 여기 들어가 있다.

안전은 마지막 블록에서 감시한다. 남북의 초록 계열 등화를 모으고 동서의 초록 계열을 모아서 둘이 동시에 켜지면 충돌로 판정한다. 설계상 상태가 배타라 정상적으로는 절대 일어나지 않는다. 설계 문서에는 그런데도 이 감시를 두는 까닭이 적혀 있다. 일어났다면 하드웨어나 배선이 고장난 것이고 그때 마지막으로 이 감시가 전방향 적색 점멸로 떨어뜨린다.

설계 문서 끝에는 판단 기록이 붙어 있다. 왜 상태를 17개로 쪼갰는지. 동시에 비상 우선이 들어오면 왜 남북을 먼저 처리하는지. 시퀀서가 멈춘 걸 왜 시간으로 감시하지 않는지. 마지막 것의 근거가 마음에 들었다. 수동 모드와 비상 우선은 무한정 기다리는 게 정상이라 시간 감시는 오검출을 낸다. 대신 상태 비트가 전부 꺼지는 비정상만 잡는다. 이런 판단이 번호 달고 열일곱 개 남아 있다. 나중에 이 프로그램을 정비하는 사람이 왜 이렇게 했지 하고 물으면 문서가 답한다.

래더, 심볼, 배선

래더는 다섯 블록으로 나왔다. 공통, 시퀀서, 남북출력, 동서출력, 안전. 인터뷰에서 확정한 그 다섯이다. 아래는 동서출력만 펼친 화면이다. 나머지 넷은 접힌 채 헤더만 보이고 펼친 블록의 행 번호는 250을 넘어간다.

AI가 생성한 신호등 래더 중 동서출력 블록을 펼친 화면

등화마다 줄이 하나씩 붙는다. 녹과 황은 앞에서 말한 대로 상태 비트를 그대로 받는다. 동서 직진 녹은 M_P4G 줄이고(동서 우선이면 M_PMPT_EW도 함께), 황은 M_P4Y 줄이다. 적은 다르게 짰다. 녹도 황도 아니면 적이라 직진 적 줄은 앞의 녹·황 접점을 NC로 받아 만든다. 정비하다 왜 적색이냐고 물으면 답이 바로 윗줄이다.

야간과 failsafe는 여기에 점멸을 얹는다. 야간이면 동서 직진 황에 붙은 (야간 ∧ @F500R) 분기가 주도로를 황색으로 깜빡이게 하고 failsafe면 같은 식으로 직진 적이 깜빡인다. @F500R은 0.5초 주기로 켜졌다 꺼지는 특수 릴레이다.

심볼은 59개가 등록됐다. 이름에 주소가 아니라 역할이 적혀 있다.

셀렉터_자동    P0    공통/셀렉터    자동 모드 선택(NO)
남북_직진_녹   P18   남북/직진      IO8 #1 OUT2
동서_보행_적   P30   동서/보행      IO8 #4 OUT2

경로 칸이 심볼을 설비별로 묶는다. 공통·남북·동서로 갈라지고 그 아래 직진·좌회전·보행으로 다시 나뉜다. 설명 칸에는 출력 심볼마다 어느 IO-8 보드의 몇 번 채널인지가 그대로 박혀 있다. 남북_직진_녹은 IO8 #1의 OUT2다. 입력은 Read, 출력과 내부 M비트는 ReadWrite로 접근 권한이 갈린다. 사람 읽으라고 만든 이 칸들이 AI한테는 그대로 지도가 된다던 말이 이 화면이다.

AI가 등록한 심볼 59개 - 경로와 설명 칸에 설비 계층과 IO-8 채널 번호가 적혀 있다

배선은 프로젝트 페이지의 보드 구성으로 잡힌다. IO-8 보드 넉 장, 입력 8점에 출력 16점이다. 입력은 셀렉터 셋과 수동 진행 버튼, 보행 요구 둘, 비상 우선 둘로 앞 두 장(P0P7)에 들어간다. 출력은 방향마다 직진 세 색·좌회전 세 색·보행 두 색, 열여섯 등화가 네 장(P16P31)에 흩어진다. 그래서 뒤 두 장은 입력 없이 출력만 쓴다. 통신은 비어 있다. 값을 주고받을 상대가 없어서 앞서 C#을 비운 것과 같은 이유로 통신도 안 붙였다. 패널을 짤 때는 이 주소 배치와 심볼 설명의 채널 번호대로 결선하면 된다.

신호등 프로젝트의 보드 구성 - IO-8 보드 넉 장, 입력 8점 출력 16점

여기까지가 나온 것들이다. 몇 마디 고른 것치고는 과하다. 그래서 이제부터가 이번 편의 본론이다. 왜 되나.

AI가 래더를 쓸 수 있는 이유

답부터 적으면 DSL이다. AI와 래더 툴 사이에 약속한 중간 언어로, 래더 격자와 무손실로 오가는 텍스트 표기다. 왜 이런 게 필요한가.

래더는 그림이다. 그런데 3편에서 본 대로 그 그림의 실제 데이터는 격자다. 셀 하나가 위치와 종류와 코드를 갖고 가로로 이으면 직렬, 세로선으로 묶으면 병렬이다. 에디터가 그리는 그림은 이 격자를 렌더한 것이고 저장도 배포도 이 격자 데이터로 한다.

이걸 AI한테 그대로 주는 길도 있다. 격자는 JSON으로 직렬화되니 셀 목록을 통째로 넘기고 셀 목록으로 돌려받으면 된다. 그런데 이건 AI가 잘 못하는 형태의 일이다. 격자에서 로직은 셀 하나하나에 있지 않고 셀들의 배치에 들어 있다. 몇 행 몇 열에 놓였고 어떤 선으로 이어졌는지가 곧 회로다. AI는 텍스트를 다루는 물건이고 코드를 잘 짜는 것도 코드가 텍스트라서다. 좌표 무더기에서 회로를 추려내는 일도, 짠 로직을 좌표에 앉히고 선을 긋는 일도 약하다. 병렬 분기 하나를 끼워 넣으면 아래 줄들의 좌표가 전부 밀리는데 한 칸만 어긋나도 다른 회로가 된다.

그래서 둘 사이에 DSL을 만들었다. 격자에서 배치를 걷어내고 로직만 남긴 텍스트다. 직렬은 줄 순서로, 병렬은 분기 블록으로 적는다. 좌표는 어디에도 없다. 신호등 래더에서 한 줄을 꺼내 보면 이렇다. 에디터가 그리는 그림.

  M남북녹계열   M동서녹계열                     M충돌감지
────┤ ├──────────┤ ├───────────────────────────( )

같은 줄을 DSL로 적으면 이렇다. 설계의 말, DSL의 텍스트, 에디터의 그림이 같은 한 줄기다.

RUNG "상충 동시녹 감지"
  IN_A M남북녹계열
  IN_A M동서녹계열
  COIL M충돌감지
END

접점 하나, 코일 하나가 텍스트 한 줄씩이다. 병렬 분기도 마찬가지다. 위아래로 묶이는 OR 회로가 텍스트에서는 분기 블록이 된다.

  셀렉터_자동                                    M아무모드
────┤ ├────┬──────────────────────────────────( )
  셀렉터_야간│
────┤ ├────┤
  셀렉터_수동│
────┤ ├────┘
RUNG "모드 선택"
  PARALLEL
    BRANCH
      IN_A 셀렉터_자동
    BRANCH
      IN_A 셀렉터_야간
    BRANCH
      IN_A 셀렉터_수동
  END
  COIL M아무모드
END

배치는 그럼 누가 하나. 변환기가 한다. AI가 DSL을 쓰면 먼저 문법 검사를 거치고 통과하면 변환기가 셀을 앉히고 선을 그어 격자로 만든다. 에디터에는 그림으로 나타난다. 반대 방향도 된다. 그려진 래더를 DSL로 꺼내 고치고 다시 넣는다. 어느 방향으로 오가든 내용이 잘리거나 뭉개지지 않는다. 이 무손실 왕복 덕에 사람이 그림으로 그려 둔 래더를 AI가 텍스트로 이어받아 손볼 수도 있다. 사람은 언제나 그림을 보고 AI는 텍스트를 쓴다.

그래서 사람이 AI 작업 중간에 끼어들어도 괜찮다. AI는 래더를 고칠 때마다 현재 상태를 통째로 다시 읽어 그 위에 이어 쓴다. 사람이 에디터에서 몇 줄 직접 고쳐 두면 다음 차례의 AI는 그 고친 것을 읽고 이어간다. 사람 손을 덮어쓰지 않는다. @행 앵커가 줄마다 제자리를 잡고 있어 텍스트로 한 바퀴 돌아 나와도 사람이 달아 둔 주석과 워치, 렁 배치가 그대로 남는다. 한 래더를 사람과 AI가 번갈아 만져도 서로의 작업이 뭉개지지 않는다.

DSL에는 주소도 잘 안 보인다. 위 텍스트에 P0 같은 주소가 없고 셀렉터_자동, M남북녹계열 같은 심볼 이름이 있다. 3편에서 심볼을 다룰 때 이렇게 적어 뒀다. 주소·경로·설명 같은 칸은 사람 좋으라고만 있는 게 아니라, AI가 래더와 코드를 만들고 분석할 때 이 심볼 정보를 근거로 삼는다고. 그 말이 여기서 그대로 실행된다. 심볼에 적힌 경로와 설명이 AI한테는 이 신호가 어느 설비의 무엇인지 알려주는 지도다. 사람 읽으라고 만든 체계가 AI한테도 그대로 먹힌다.

그러고 보면 DSL은 AI가 좌표를 못 다뤄서 어쩔 수 없이 만든 우회로가 아니다. 사람과 AI가 같은 래더를 번갈아 쥐게 하는 통로다.

아는 것만 쓴다

DSL로 쓸 수 있다는 것과 제대로 쓴다는 건 다른 문제다.

AI의 훈련 데이터에 우리 래더 표기는 없고 펑션 목록도 주소 체계도 없다. 이럴 때 AI가 하는 일은 정해져 있다. 아는 것처럼 지어낸다. 문제는 지어낸 게 그럴듯하다는 데 있다. 존재하지 않는 펑션, 잘못된 주소 표기. 이런 건 생김새가 멀쩡해서 빌드를 통과할 수도 있다. 빌드가 못 잡는 가짜다.

그래서 규칙을 하나 박았다. 문서에서 확인한 것만 쓴다. Senbrix에는 래더 문법, 펑션 목록, 주소 체계, 심볼 규칙을 정리한 문서가 내장되어 있고 AI는 래더에 무언가를 쓰기 전에 이 문서를 조회해서 실제로 있는지 확인해야 한다. 문서에 없으면 아는 것으로 치지 않는다. 지어내는 대신 모른다고 하고 묻는다.

문서에 문법만 있는 게 아니다. 안전 수칙과 설계 방법이 같이 들어 있다. 입력이 끊겼을 때 안전한 쪽으로 떨어지게 신호를 잡는 법. 출력 하나에 코일은 하나만 두는 것. 비상을 해제했다고 기계가 저절로 재기동하면 안 된다는 것. 상태기계를 래더로 옮기는 표준형. 현장에서 당연하게 여기는 것들을 문서로 만들어 두면 AI가 설계할 때 그 당연함을 근거로 쓴다. 신호등의 안전 골격이 어디서 나왔냐고 물으면 답은 이 문서들이다.

조회한 흔적은 계획서에 남는다. 신호등 계획서에는 쓰려는 것마다 출처를 적은 표가 들어갔다.

쓰는 것표기출처
타이머TAON, 100ms 단위문법 문서
특수 릴레이@BEGIN(첫 사이클), @F500R(점멸)심볼 문서
안전 원칙신호 부재는 안전측으로안전 문서

출처 없는 항목: 없음.

출처 표가 첫째 장치라면 둘째 장치는 구현 항목이다. 설계의 블록마다 구현 항목을 만들어 progress.json에 미리 박아 둔다. 각 항목에는 설계 문서의 어느 절이 근거인지, 무엇으로 검증할지가 같이 묶인다. 구현하는 AI는 이 목록에 항목을 더할 수 없고 등록된 항목을 하나씩 닫기만 한다. 신호등에서는 다섯 블록마다 이런 항목이 들어갔는데 그중 둘만 옮기면 이렇다.

항목근거닫는 조건
현시 시퀀서설계 §5.1상태 17개, 전이표 전수 대조, 빌드 통과
안전 인터록설계 §5.4충돌 감지, 유령 상태 감지, 맨 뒤 배치, 빌드 통과

이게 왜 필요한가. 안전 인터록을 TODO로 비워 두고 완료라고 표시하는 상황을 막기 위해서다. 현장에서는 누락이 곧 사고다. 항목이 미리 박혀 있으면 빼먹은 건 열린 채로 남고 껍데기만 채운 건 설계와 대조할 때 걸린다.

계획 단계 진행표 - 블록별 구현 항목과 출처 표

하나는 지어내기를 막고 다른 하나는 빼먹기를 막는다. 이 두 장치가 계획서에 들어가고 계획이 승인되어야 구현이 시작된다.

걸러진 것들

만든 것을 거르는 층이 여럿 있다. 층마다 잡는 게 다르고 이번 신호등에서 층마다 실제로 하나씩 잡았다. 시간 순서 말고 층 순서로 적는다.

기계가 첫 층이다. 빌드가 문법을 잡는다. 그 위에 래더 구조 검사가 돈다. 출력에 닿지 못하는 끊긴 줄, 같은 코일에 겹쳐 쓰는 출력. 이런 배선 문제를 기계가 훑는다. 이 검사는 에디터에서 저장할 때도 돌아서 짜는 중에도 걸리는 게 있으면 배지로 뜬다.

둘째 층은 대조다. 짠 래더를 설계·계획 문서와 맞춰 보는 검수가 돈다. 앞 절에서 구현 항목마다 “무엇으로 검증할지”를 미리 박아 뒀는데 그 채점표가 여기서 실제로 돌아간다. 기준은 짜기 전에 세우고 채점은 짠 뒤에 한다. 이 검수는 구현한 흐름과 분리해서 돌린다. 자기가 짠 걸 자기가 채점하면 후하게 주기 마련이라 검증하는 쪽은 설계와 계획을 처음부터 다시 읽고 산출물과 대조한다.

이 대조에서 하나 걸렸다. 계획서에는 상태 전이 줄을 역순으로 배치하라는 요구가 있었다. 나중 상태의 전이를 위에 두는 배치인데 여기엔 이유가 있다. 전이 줄이 진행 순서대로 늘어서 있으면 조건이 겹치는 순간 한 사이클에 상태를 두 칸 건너뛸 수 있다. 구현된 래더는 이 요구와 달리 진행 순서대로 늘어서 있었다. 빌드는 통과했고 구조 검사도 깨끗했다. 문법으로도 배선으로도 문제가 없으니까. 계획과 대조한 검수만 이걸 잡았고 전이 줄 네 개를 재배치한 뒤에야 항목이 닫혔다.

검증 체크리스트 - 계획과 대조해 상태 전이 줄 배치를 잡아낸 항목

사람이 마지막 층이다. 어떻게 시키나 절에서 미뤄 둔 얘기가 이것이다. 설계 문서가 승인 대기로 멈춰 있을 때 그냥 승인하지 않고 훑었다. 두 군데가 걸렸다.

하나는 야간 등화 표다. 야간 점멸에서 주도로인 동서는 황색 점멸이어야 하는데 표에 적색 점멸로 적혀 있었다. 그대로 구현됐으면 주도로가 정지 신호로 뒤바뀔 뻔했다. 또 하나는 셀렉터 전환이다. 로터리 셀렉터는 돌리는 순간 접점이 잠깐 전부 떨어진다. 그 찰나를 단선으로 판정하면 모드를 바꿀 때마다 고장으로 떨어진다. 판정에 시간 여유를 두라고 돌려보냈다. 반려하는 김에 결정도 하나 얹었다. 비상 우선이 들어와도 방금 켜진 초록을 바로 끄지 말고 최소 5초는 유지할 것. 초록이 켜지자마자 꺼지는 교차로를 만들 수는 없다.

AI는 세 건을 모두 수긍하고 설계를 고쳐 왔다. 야간 표가 바로잡히고 판정 지연이 들어갔다. 최소 녹색 시간도 상태기계에 추가됐다. 다시 확인했고 내가 보낸 승인은 한 단어였다.

승인

설계 단계 진행표 - 반려 세 건을 반영하고 승인된 상태

층마다 잡은 걸 나란히 놓으면 이렇다. 빌드는 문법을, 구조 검사는 배선을, 문서 대조는 약속 위반을 잡았다. 판단 오류는 사람이 잡았다. 야간 등화 표는 문법도 배선도 멀쩡한 설계 오류라 기계까지 내려가지도 않았다. 어느 한 층도 나머지를 대신하지 못하니 층으로 쌓는다.

통제권은 사람

정리하면 이 흐름에서 AI 혼자 넘을 수 없는 지점이 세 군데다.

설계 승인. 계획 승인. 그리고 마지막이 배포다.

앞의 둘은 이미 봤다. 문서가 승인 대기로 멈추면 사람이 읽고 반려하면 고치고 승인해야 넘어간다. 마지막 배포는 4편에서 다룬 그 자리다. 빌드된 앱을 장비에 넣는 Deploy 버튼은 에디터에 있고 그걸 누르는 건 사람이다. AI가 짠 래더가 사람 확인 없이 기계에 실리는 경로는 없다.

반려가 나왔다고 워크플로우가 실패한 건 아니다. 야간 등화 표를 돌려보낸 그 순간이야말로 이 구조가 작동한 증거다. 멈추라고 만든 자리에서 멈췄고 고치라고 둔 경로로 고쳐졌다.

검증까지 끝나면 AI 모드 자체가 내려간다. 처음에 말한 그 갈림길로 돌아가는 것이다. 이 프로젝트는 이제 그냥 시작한 프로젝트와 똑같고 래더를 열어 사람이 이어서 그리면 된다. AI를 다시 부를 수도 있다. 그때의 AI는 어시스턴트다. 창작은 끝났고 사람이 시키는 수정과 검토를 하는 자리로 내려와 있다.

그러니 이렇게 말하는 게 정확하다. AI가 래더를 짜는 게 아니라, 사람이 AI로 래더를 짠다. AI가 내는 건 초안과 속도까지고 판단과 완성은 사람이 쥔다.

정리

끝나고 나서 세션 기록을 열어 내가 입력한 걸 세어 봤다. 이 프로젝트를 만드는 동안 키보드로 친 메시지가 넉 줄이다. 시작하라는 지시 한 줄, 설계 반려 한 건, 승인, 진행. 나머지는 전부 카드에서 고르는 클릭이었다. 나온 건 래더 85줄짜리 신호 제어기 한 벌이다.

몇 마디 넣었는데 제어기가 나왔다. 오 이게 되네.

왜 되는지도 이제 답할 수 있다. 래더를 텍스트로 쓸 수 있게 언어를 만들었고 문서에서 확인한 것만 쓰게 막았다. 단계마다 사람이 승인하게 세웠다. 이 세 가지가 전부고 신기한 재주는 없다.

1편에서 코드를 이렇게 잘 짜는데 래더라고 못 할까 하고 적었다. 답을 얻었다. 된다. 단, 짜는 건 AI고 시키고 거르고 완성하는 건 사람이다.

다음 편

여기까지가 AI가 래더를 짜는 이야기였다. 다음 편은 돌고 있는 PLC를 AI가 들여다보는 이야기다. 값을 읽고 막힌 원인을 짚고 고치기까지 한다. 물론 그것도 사람이 허락한 범위 안에서만.