감각에서 사고와 행동까지 이어지는 대뇌피질의 기능적 구조

인간의 뇌를 공부할 때 가장 흔하게 생기는 오해는 뇌를 여러 개의 독립된 “기능 상자”로 생각하는 것이다.

시각은 뒤통수에 있고, 언어는 왼쪽에 있으며, 사고는 앞쪽에 있고, 운동은 중앙에 있다는 식이다.

해부학을 처음 공부할 때는 이런 구분이 필요하다. 그러나 실제 뇌가 작동하는 방식은 전혀 그렇게 단순하지 않다.

컵 하나를 집는 행동만 해도 시각피질, 두정엽, 전전두피질, 전운동피질, 일차운동피질, 기저핵, 소뇌, 척수, 체성감각계가 동시에 관여한다.

말 한 문장을 이해하는 것 역시 청각피질에서 끝나지 않는다. 소리의 시간적 패턴을 분석하고, 음성을 음소와 단어로 분절하고, 단어의 의미를 검색하고, 문장 내부의 문법적 관계를 계산하며, 그 문장을 현재 상황과 기억에 연결해야 한다.

따라서 인간 뇌의 고등 기능을 이해하려면 “어느 부위가 무엇을 담당하는가”보다 한 단계 더 깊은 질문이 필요하다.

“어떤 정보가 어느 회로를 지나면서 어떤 형태로 변환되는가?”

이 질문을 중심으로 보면 시각, 언어, 사고, 운동, 기억, 시퀀스가 서로 별개의 주제가 아니라 하나의 연속된 계산 과정으로 연결된다.


---

브로드만 지도는 기능 지도가 아니다

강의에 등장하는 숫자들은 브로드만 영역(Brodmann area)을 뜻한다.

브로드만 지도는 독일의 신경해부학자 코르비니안 브로드만(Korbinian Brodmann)이 대뇌피질을 현미경으로 관찰하고, 신경세포의 배열과 층 구조가 달라지는 경계를 기준으로 분할한 지도다.

중요한 점이 있다.

브로드만은 “여기는 언어 영역”, “여기는 창의성 영역”이라고 기능을 측정해서 번호를 붙인 것이 아니다.

대뇌피질을 구성하는 세포의 크기, 밀도, 층의 두께, 피질 내부 구조가 달라지는 지점을 구분했다.

그 후 수십 년에 걸친 병변 연구, 뇌 자극 연구, 전기생리학, PET, fMRI, 확산 MRI 연구를 통해 각 영역의 기능이 밝혀졌다.

따라서 브로드만 번호와 기능 사이에는 상당한 대응관계가 있지만 완전한 일대일 대응은 아니다.

현대 신경과학에서는 같은 브로드만 영역 안에서도 기능과 연결성이 다른 여러 하위 영역을 구분한다.

이 책에서는 그래서 브로드만 번호보다 해부학적 이름을 우선한다.

후두엽의 일차시각피질, 상두정소엽, 일차청각피질, 하전두회, 배외측 전전두피질, 전두극피질처럼 실제 해부학적 위치를 먼저 이해해야 한다.


---

시각은 눈에서 만들어지는 것이 아니라 뇌에서 구성된다

사람은 흔히 눈으로 사물을 본다고 생각한다.

물리적으로는 그렇지 않다.

눈에 들어오는 것은 “자동차”, “나무”, “친구 얼굴”이 아니다.

망막에 도달하는 것은 다양한 파장을 가진 광자다.

망막의 광수용체가 빛을 전기화학적 신호로 바꾸고, 망막 내부 회로가 이를 가공한 뒤 시신경을 통해 뇌로 전달한다.

시신경의 상당수 신호는 시상(thalamus)에 위치한 외측슬상핵(lateral geniculate nucleus, LGN)을 거친다.

이후 시각방사(optic radiation)를 따라 후두엽 안쪽의 일차시각피질(primary visual cortex, V1)로 들어간다.

전통적 브로드만 지도에서는 이 영역을 BA17이라고 부른다.

여기서 중요한 변화가 일어난다.

외부 세계의 빛 신호가 뇌 내부의 공간적 신경활동 패턴으로 변환된다.


---

일차시각피질은 ‘사물을 인식하는 곳’이 아니다

후두엽의 일차시각피질은 우리가 보는 장면 전체의 의미를 해석하지 않는다.

여기에서는 훨씬 기초적인 계산이 이루어진다.

경계가 어느 방향으로 놓여 있는지, 밝기 대비가 어디에서 달라지는지, 특정 공간 위치에 어떤 시각 특징이 존재하는지 같은 정보가 처리된다.

예를 들어 우리가 검은 배경 위의 흰색 세로선을 본다고 하자.

의식적으로는 그냥 “흰 선”을 본다.

그러나 피질에서는 선의 위치, 방향, 공간주파수, 대비 같은 여러 특성이 서로 다른 뉴런 집단에 의해 병렬적으로 처리된다.

즉 뇌는 처음부터 “물체”를 받지 않는다.

빛의 공간적 패턴을 여러 기본 특징으로 분해한 뒤 다시 조합해 물체라는 표상을 만든다.

이 원리는 시각을 이해하는 데 매우 중요하다.

지각(perception)은 외부 세계가 그대로 뇌 안으로 복사되는 과정이 아니다.

뇌가 제한된 감각 데이터를 이용하여 외부 세계의 원인을 추정하는 과정이다.


---

초기 시각피질에서 고차 시각피질로

일차시각피질 주변에는 이차 및 고차 시각피질이 넓게 펼쳐져 있다.

전통적 브로드만 분류에서 BA18, BA19라고 불렸던 영역들이 여기에 상당 부분 포함된다.

하지만 현대 신경과학에서는 단순히 “18번”, “19번”이라고 부르기보다 V2, V3, V4, MT/V5 등 기능적으로 구분된 시각영역을 사용한다.

여기에서 시각정보는 점차 더 복합적인 형태로 재구성된다.

단순한 경계에서 형태로,

형태에서 물체로,

움직임의 국소 변화에서 전체 움직임으로,

색의 물리적 파장에서 안정적인 색 지각으로 발전한다.

그러면서 시각계는 크게 두 방향의 정보 흐름을 형성한다.


---

복측 시각경로: “무엇인가”

후두엽에서 측두엽 방향으로 진행하는 경로를 복측 시각경로(ventral visual stream)라고 한다.

전통적으로 “what pathway”라고 불렸다.

여기서는 물체가 무엇인지 식별하는 과정이 발달한다.

컵인지,

얼굴인지,

글자인지,

자동차인지,

동물인지

같은 범주가 점차 구분된다.

측두엽 아래쪽으로 갈수록 단순한 선의 방향보다는 복잡한 형태와 객체 수준의 표상이 나타난다.

얼굴처럼 매우 복잡한 시각 자극을 처리하는 영역도 이 계통에 속한다.

따라서 우리가 사람을 바라볼 때 실제로 뇌에서 벌어지는 일은

빛 → 경계 → 형태 → 부분들의 관계 → 물체 혹은 얼굴의 정체성

이라는 단계적 변환에 가깝다.


---

배측 시각경로: “어디에 있으며 어떻게 행동할 것인가”

후두엽에서 두정엽 방향으로 진행하는 경로가 배측 시각경로(dorsal visual stream)다.

예전에는 단순히 “where pathway”라고 불렀지만 현대적으로는 “vision for action”, 즉 행동을 위한 시각이라는 표현이 더 적절하다.

컵을 알아보는 것과 컵을 집는 것은 다른 문제다.

컵을 집으려면 뇌는

컵의 위치,

손의 현재 위치,

컵까지의 거리,

컵의 크기,

손가락을 얼마나 벌려야 하는지,

팔을 어느 방향으로 이동해야 하는지

를 동시에 계산해야 한다.

이때 두정엽의 역할이 매우 중요하다.


---

상두정소엽과 후두정피질: 시각을 행동 좌표로 변환한다

두정엽 위쪽에는 상두정소엽(superior parietal lobule)이 있다.

전통적 브로드만 지도에서는 상당 부분이 BA7에 해당한다.

이 영역을 단순히 “감각 통합 영역”이라고 부르는 것보다 그 계산의 본질을 이해하는 것이 중요하다.

뇌에 들어오는 좌표들은 처음부터 같은 좌표계가 아니다.

망막에는 망막 기준의 좌표가 있다.

눈의 위치가 변하면 같은 물체도 망막에서는 다른 위치에 투사된다.

머리의 방향도 변한다.

몸통의 방향도 변한다.

팔과 손의 위치도 계속 바뀐다.

그런데 우리는 눈을 움직이면서도 “컵이 이동했다”고 착각하지 않는다.

후두정피질은 시각정보, 눈의 위치, 머리 위치, 고유수용감각(proprioception), 신체 위치 정보를 결합하여 이러한 좌표들을 서로 변환한다.

그래서

“망막의 오른쪽에 있는 물체”



“내 몸을 기준으로 오른쪽 30도에 있는 물체”

로 바뀌고,

다시

“오른손을 약 40cm 뻗으면 잡을 수 있는 위치”

로 변환된다.

여기서 이미 지각과 운동은 분리되어 있지 않다.

우리가 보는 공간은 처음부터 행동할 수 있는 공간으로 재구성되고 있다.


---

공간기억은 후두엽에 저장되는 것이 아니다

장소를 기억하는 능력, 즉 토포그래피컬 메모리(topographical memory)를 시각피질만으로 설명할 수는 없다.

익숙한 동네에서 길을 찾는 상황을 생각해 보자.

우리는 건물을 알아봐야 한다.

현재 방향을 알아야 한다.

내가 조금 전에 어디에서 왔는지 기억해야 한다.

목적지가 어느 방향인지 추론해야 한다.

필요하면 머릿속에서 경로를 바꿔야 한다.

이 과정에는 여러 계통이 협력한다.


---

해마: 사건과 공간의 관계를 묶는다

측두엽 안쪽 깊숙한 곳에 있는 해마(hippocampus)는 공간기억과 일화기억(episodic memory)에 핵심적이다.

해마에는 특정 환경의 특정 장소에서 선택적으로 활성화되는 장소세포(place cell)가 발견된다.

중요한 것은 해마를 단순히 “장소 저장소”라고 이해하지 않는 것이다.

해마는

어디에서,

언제,

무엇을 경험했는가

라는 관계를 묶는 시스템에 가깝다.

즉 독립된 정보들을 하나의 사건 구조로 연결한다.


---

내후각피질: 공간의 좌표계

해마 주변의 내후각피질(entorhinal cortex)에는 격자세포(grid cell)를 포함한 공간표상 뉴런들이 존재한다.

격자세포는 특정 장소 하나에만 반응하는 것이 아니라 공간을 이동할 때 규칙적인 격자 패턴으로 활성화된다.

그래서 내후각피질-해마 회로는 공간 위치와 이동을 표현하는 하나의 신경 좌표계로 연구되고 있다.

이 부분은 강의에서 말한 “공간적 표상”을 이해하는 데 훨씬 중요한 신경과학적 배경이다.


---

후방비장피질: 내가 보는 방향과 기억 속 지도를 연결한다

뇌 뒤쪽 안쪽 면에는 후방비장피질(retrosplenial cortex)이 있다.

이 영역은 랜드마크와 방향, 익숙한 장소의 공간 관계를 연결하는 데 중요한 역할을 한다.

예를 들어 지도를 머릿속에 알고 있는데 현재 어느 방향을 보고 있는지를 연결하려면 좌표계 변환이 필요하다.

현재 시야는 자기중심적(egocentric)이다.

“문이 내 오른쪽에 있다.”

반면 장기적인 공간 지도는 환경중심적(allocentric)이다.

“문은 건물의 북쪽에 있다.”

후방비장피질과 해마-두정계는 이런 서로 다른 공간표상을 연결한다.

따라서 장소기억은 어느 한 뇌 부위에 저장된 지도 한 장이 아니다.

시각계가 장면을 분석하고,

두정엽이 현재 몸과 공간의 관계를 계산하고,

후방비장피질이 방향과 좌표를 연결하고,

해마-내후각계가 장소와 사건의 관계를 조직한다.


---

청각계에서는 시간이 공간만큼 중요하다

시각은 공간적으로 펼쳐진 정보를 한 번에 많이 받을 수 있다.

반면 말소리는 본질적으로 시간 속에서 나타난다.

“사과”라는 단어의 음향 정보가 한 번에 귀에 들어오는 것이 아니다.

시간에 따라 음압이 변하고, 그 변화가 연속적으로 귀에 도달한다.

언어를 이해하려면 뇌가 이 시간적 구조를 매우 정밀하게 분석해야 한다.


---

일차청각피질

귀에서 발생한 신경신호는 뇌간의 여러 청각핵과 중뇌의 하구(inferior colliculus), 시상의 내측슬상체(medial geniculate body)를 거쳐 측두엽의 청각피질로 들어간다.

헤슐회(Heschl's gyrus)를 중심으로 일차청각피질(primary auditory cortex)이 자리한다.

전통적 브로드만 분류에서는 주로 BA41이 이 영역과 대응한다.

여기에서는 주파수, 음의 세기, 시간적 변화 같은 기본적인 음향 특징들이 분석된다.

그러나 “여기는 tone만 처리한다”고 말하면 지나치게 단순하다.

청각신호는 달팽이관 단계부터 이미 주파수별로 분리되며, 뇌간과 시상에서도 시간과 강도, 위치에 관한 분석이 상당 부분 진행된다.

청각피질은 그 위에 더 복잡한 시간적 패턴을 구성한다.


---

청각연합피질에서 음성언어로

일차청각피질 주변의 청각연합피질은 더 복잡한 소리의 패턴을 분석한다.

전통적 브로드만 지도에서 BA42와 상측두회(superior temporal gyrus)의 여러 영역이 이 과정에 포함된다.

말을 들을 때 뇌가 해야 하는 계산은 매우 어렵다.

실제 음성에는 단어 사이에 깔끔한 공백이 없다.

화자마다 음높이와 발음이 다르다.

말하는 속도도 다르다.

같은 음소도 앞뒤 음소에 따라 물리적 형태가 달라진다.

그럼에도 뇌는 연속적인 음향파형에서 언어적으로 중요한 패턴을 추출한다.



음향 특징

→ 음성 특징

→ 음소 패턴

→ 음절

→ 단어

→ 문장

으로 점차 추상화가 이루어진다.


---

베르니케 영역이라는 오래된 개념

고전 신경학에서는 왼쪽 상측두회 뒤쪽을 베르니케 영역(Wernicke's area)이라고 불렀다.

전통적 브로드만 분류에서는 주로 후방 BA22와 연관된다.

여기서 반드시 바로잡아야 할 점이 있다.

BA42 자체를 베르니케 영역이라고 동일시해서는 안 된다.

더 나아가 현대 신경과학은 “베르니케 영역 하나가 언어 이해를 담당한다”는 모델도 그대로 받아들이지 않는다.

언어 이해는 상당히 넓은 측두엽-전두엽 네트워크에서 수행된다.

말소리를 분석하는 회로,

단어 의미를 검색하는 회로,

문장의 문법 관계를 계산하는 회로,

담화의 맥락을 통합하는 회로가 서로 부분적으로 구분된다.

베르니케 영역은 역사적으로 중요한 개념이지만 오늘날 언어 이해를 설명하는 완전한 모델은 아니다.


---

브로카 영역 역시 단순한 ‘말하기 중추’가 아니다

왼쪽 하전두회(inferior frontal gyrus)의 뒤쪽에는 브로카 영역(Broca's area)이 있다.

해부학적으로는 주로 pars opercularis와 pars triangularis로 나뉜다.

전통적 브로드만 분류에서는 각각 BA44와 BA45에 상당 부분 대응한다.

과거에는 브로카 영역을 언어 생산 중추라고 단순하게 설명했다.

그러나 현재는 훨씬 복잡하게 이해한다.

뒤쪽의 pars opercularis는 음운 처리, 조음과 관련된 운동계획, 구문 처리와 비교적 밀접한 관계를 보인다.

조금 앞쪽의 pars triangularis는 어휘와 의미 선택, 의미적 경쟁 해결 등에 더 많이 관여하는 경향이 있다.

하지만 두 영역 모두 언어 산출과 이해 과정에 참여할 수 있다.

따라서

“뒤쪽 브로카 영역은 감정적으로 튀어나오는 말을 담당한다”

“앞쪽 브로카 영역은 완성된 문장을 담당한다”

처럼 기능을 깔끔하게 둘로 나누는 설명은 현대 신경과학적으로 지나치게 단순하다.


---

자발적인 감정 발화는 어디에서 나오는가

욕설이나 감탄사처럼 감정적으로 튀어나오는 발화가 계획된 문장발화와 어느 정도 다른 회로를 이용하는 것은 맞다.

그러나 그것을 브로카 영역의 특정 부분 하나로 환원할 수는 없다.

자발적 감정 발화에는 변연계(limbic system), 대상피질(cingulate cortex), 섬엽(insula), 기저핵(basal ganglia), 우반구의 운율(prosody) 처리 계통 등이 함께 영향을 준다.

언어는 단순히 “단어를 생성하는 장치”가 아니다.

의미, 감정, 사회적 상황, 발성운동, 호흡조절이 결합된 행동이다.


---

언어의 본질에는 시퀀스가 있다

강의에서 가장 통찰력 있는 부분 가운데 하나는 언어를 시퀀스(sequence)의 관점에서 보는 것이다.

말은 시간 속에서 나타난다.

음소가 배열되고,

음소가 음절을 만들고,

음절이 단어를 만들고,

단어가 문장을 만든다.

“개가 사람을 물었다.”

“사람이 개를 물었다.”

같은 단어를 사용해도 배열이 달라지면 의미가 달라진다.

따라서 언어에서 순서는 본질적이다.

그러나 인간 언어의 핵심을 시퀀스 하나로만 설명해서는 부족하다.


---

인간 언어는 순서뿐 아니라 계층구조를 처리한다

문장은 단어들의 일렬 배열이 아니다.

예를 들어

“철수가 영희가 샀다고 말한 책을 읽었다.”

라는 문장에서

“영희가 샀다고 말한”

부분은 “책”이라는 명사를 수식하는 하나의 구조를 이룬다.

뇌가 처리해야 하는 것은

단어 1 → 단어 2 → 단어 3

라는 단순한 시간적 연결이 아니다.

어떤 단어가 어떤 단어에 종속되고,

어떤 구가 다른 구 안에 포함되며,

주어와 술어가 어떤 관계를 이루는지를 계산해야 한다.

따라서 인간 언어의 계산 구조는

시퀀스

계층구조

관계


를 함께 포함한다.

이 점은 인간 지능 전체를 이해할 때도 매우 중요하다.

고등사고는 정보를 순서대로 나열하는 능력이 아니라 관계를 가진 구조로 묶고, 필요할 때 그 구조를 다시 구성하는 능력이다.


---

배외측 전전두피질과 ‘구성적 사고’

전두엽 앞쪽의 바깥면에는 배외측 전전두피질(dorsolateral prefrontal cortex, DLPFC)이 있다.

전통적 브로드만 분류에서는 BA9와 BA46의 상당 부분이 여기에 포함된다.

이 영역은 인간의 고등 인지기능을 이해하는 데 핵심적이다.

그러나 “46번 영역이 구성적 사고의 중추”라고 단정하기보다는 그 표현이 가리키는 계산을 해부해보는 것이 훨씬 유용하다.

배외측 전전두피질은 작업기억(working memory)과 인지통제(cognitive control)에 깊게 관여한다.

작업기억은 단순히 짧게 기억하는 기능이 아니다.

현재 문제를 해결하기 위해 필요한 정보를 잠시 유지하면서 동시에 조작하는 능력이다.

예를 들어

7, 2, 9라는 숫자를 들은 뒤

9, 7, 2 순으로 바꾸라고 하면

뇌는 정보를 저장하는 데서 끝나지 않는다.

기존 배열을 유지하면서 순서를 다시 구성해야 한다.

이런 계산에 배외측 전전두피질과 두정피질이 강하게 관여한다.


---

구성적 사고는 특정 부위가 아니라 전두-두정 네트워크의 기능이다

새로운 문제를 해결하려면

관련 정보를 기억에서 불러오고,

중요한 정보만 작업기억에 유지하고,

불필요한 반응을 억제하고,

여러 관계를 비교하고,

현재 규칙을 유지하고,

필요하면 규칙 자체를 바꿔야 한다.

이 과정은 하나의 뇌 영역으로 설명되지 않는다.

배외측 전전두피질과 두정연합피질을 중심으로 하는 전두-두정 네트워크(frontoparietal network)가 핵심적으로 관여한다.

따라서 강의에서 말한 constructive thinking, 즉 구성적 사고를 현대 신경과학적으로 다시 정의하면 다음에 가깝다.

이미 존재하는 기억과 현재 입력을 작업기억 안에서 유지하면서, 요소들의 관계와 순서를 목적에 맞게 재조직하여 새로운 내부 모델을 만드는 과정.

이것은 단순 기억과 질적으로 다르다.


---

전두극피질은 미래의 목표를 다룬다

전전두피질에서 가장 앞쪽에 위치하는 부분이 전두극피질(frontopolar cortex)이다.

전통적 브로드만 분류에서는 BA10과 상당 부분 대응한다.

강의에서는 이 영역을 행동 시퀀스와 연결했지만, 현대 연구에서 더욱 안정적으로 나타나는 기능은 조금 다르다.

전두극피질은 현재 수행 중인 행동을 넘어서 더 상위의 목표를 유지하거나 여러 목표 사이를 조직하는 데 관여한다.

예를 들어 책을 읽다가

“30분 후 오븐을 꺼야 한다”

는 사실을 유지한다고 하자.

현재 읽고 있는 문장의 의미는 작업기억에서 처리되고 있다.

그러나 동시에

“나중에 오븐을 끈다”

라는 아직 실행하지 않는 목표도 유지해야 한다.

이런 미래 의도를 전향기억(prospective memory)이라고 한다.

전두극피질은 이러한 미래 목표, 관계 통합, 여러 작업 사이의 branching과 깊게 연관된다.

따라서 이 영역은 단순한 “행동 순서 중추”보다는

여러 시간척도의 목표를 동시에 관리하는 고차 전전두 시스템

으로 이해하는 편이 정확하다.


---

동기는 전두엽 한 부위에서 만들어지지 않는다

강의에서 특정 전두영역을 motivation과 연결한 설명도 네트워크 수준으로 확장할 필요가 있다.

동기가 생긴다는 것은 단순히 “하고 싶다”는 느낌을 만드는 과정이 아니다.

뇌는 최소한 다음을 계산해야 한다.

그 행동을 했을 때 얻는 보상은 얼마나 큰가.

얼마나 많은 노력이 필요한가.

성공할 가능성은 어느 정도인가.

더 좋은 선택지가 있는가.

지금 행동할 가치가 있는가.

이 계산에는 복내측 전전두피질(ventromedial prefrontal cortex), 안와전두피질(orbitofrontal cortex), 전방 대상피질(anterior cingulate cortex), 복측선조체(ventral striatum), 중뇌 도파민계가 중요하게 관여한다.

따라서 motivation은 특정 브로드만 번호에 대응하는 기능이 아니다.

가치, 노력, 보상예측, 행동 선택을 통합하는 회로 전체의 상태다.


---

운동은 생각의 마지막 단계가 아니다

운동을 흔히 “뇌가 생각한 것을 몸이 실행하는 과정”이라고 설명한다.

실제 운동제어는 훨씬 역동적이다.

행동하는 동안에도 뇌는 끊임없이 감각정보를 받고 계획을 수정한다.

손으로 컵을 집는 동안 손가락이 컵 표면에 닿는 순간 촉각이 변한다.

컵이 예상보다 무겁다면 근육 힘을 증가시킨다.

컵이 미끄러지면 손가락 압력을 조절한다.

즉 운동은

계획 → 실행

이라는 단방향 명령이 아니라

예측 → 움직임 → 감각 피드백 → 오차 계산 → 수정

이라는 반복적인 폐쇄루프 제어다.


---

일차운동피질

중심고랑(central sulcus) 바로 앞의 중심앞이랑(precentral gyrus)에 일차운동피질(primary motor cortex, M1)이 있다.

전통적 브로드만 분류에서는 BA4에 해당한다.

이곳은 피질척수로(corticospinal tract)를 통해 척수 운동계에 강한 영향을 미치며 특히 손과 손가락처럼 정교한 수의운동에 중요하다.

몸의 각 부분은 운동피질에 어느 정도 체계적으로 배열되어 있다.

이를 운동 호문쿨루스(motor homunculus)라고 표현한다.

그러나 사람 그림처럼 깔끔하게 근육 하나하나가 분리되어 있는 것은 아니다.

실제 운동피질 뉴런은 하나의 근육보다는 여러 근육으로 구성된 움직임과 관계되는 경우가 많다.


---

전운동피질과 보조운동영역

일차운동피질 바로 앞쪽에는 전운동피질(premotor cortex)과 보조운동영역(supplementary motor area, SMA)이 있다.

전통적 브로드만 분류에서 상당 부분이 BA6에 속한다.

이 영역은 이미 완성된 운동명령을 단순히 전달하는 곳이 아니다.

움직이기 전에 필요한 준비와 구조화가 이루어진다.

외부 자극에 따라 어떤 움직임을 선택할 것인지,

여러 동작을 어떤 순서로 연결할 것인지,

양손을 어떻게 협응할 것인지,

학습한 운동 패턴을 어떻게 꺼낼 것인지

같은 계산에 관여한다.

따라서 운동기술(motor skill)이라는 강의의 설명은 방향은 맞지만 “운동기술이 BA6에 저장된다”고 생각해서는 안 된다.

운동기술은 전운동피질, SMA, 일차운동피질, 기저핵, 소뇌를 포함하는 분산된 네트워크가 학습한 결과다.


---

운동 시퀀스와 청크

피아노를 처음 배울 때는 각각의 손가락 움직임을 의식적으로 생각한다.

오른손 세 번째 손가락,

다음은 다섯 번째,

다시 첫 번째.

그러나 연습이 충분히 반복되면 각각의 동작을 따로 선택하지 않는다.

여러 동작이 하나의 묶음으로 처리된다.

이를 운동 청크(motor chunk)라고 한다.

초보자는

A → B → C → D

를 네 번의 독립된 선택처럼 실행한다.

숙련자는

[A-B-C-D]

를 하나의 단위로 불러낸다.

숙련된 행동이 빠르고 안정적인 이유 중 하나가 여기에 있다.

전두피질이 세부 동작을 일일이 통제할 필요가 줄어들고, 기저핵과 운동피질, 소뇌에서 잘 학습된 패턴을 효율적으로 실행할 수 있게 된다.


---

기저핵: 어떤 행동을 선택할 것인가

기저핵(basal ganglia)은 대뇌피질 아래 깊은 곳에 위치한다.

선조체(striatum), 담창구(globus pallidus), 시상하핵(subthalamic nucleus), 흑질(substantia nigra) 등이 주요 구성 요소다.

기저핵은 단순한 운동기관이 아니다.

여러 가능한 행동 중 무엇을 선택하고 어떤 행동을 억제할지 결정하는 데 중요한 역할을 한다.

반복적인 학습을 통해 특정 행동 시퀀스가 안정되고 습관화되는 데도 깊게 관여한다.

따라서 인간의 행동 시퀀스를 설명하려면 전전두피질이나 전운동피질뿐 아니라 기저핵을 반드시 포함해야 한다.


---

소뇌: 움직임의 오차를 계산한다

뇌 뒤아래쪽의 소뇌(cerebellum)는 정교한 운동과 학습에 핵심적이다.

소뇌를 단순히 “균형을 잡는 곳”이라고 배우는 경우가 많지만 기능은 훨씬 넓다.

운동을 시작할 때 뇌는

“이 운동명령을 보내면 몸이 이렇게 움직일 것이다”

라는 예측을 만든다.

이를 전향모델(forward model)과 연결해 설명한다.

실제 운동이 일어나면 감각계가 결과를 뇌로 돌려보낸다.

소뇌는 예상 결과와 실제 결과 사이의 차이를 이용해 다음 운동을 조정한다.

그래서 반복연습을 하면 동작이 점점 부드럽고 정확해진다.

숙련은 단순히 근육이 강해지는 현상이 아니다.

예측오차가 줄어들도록 신경계의 내부 모델이 조정되는 과정이다.


---

넘어짐 방지 반응은 전두엽 한 영역의 기능이 아니다

강의에서 전두엽의 특정 브로드만 영역을 falling response와 연결한 부분은 현대 신경과학적으로 그대로 받아들이기 어렵다.

넘어질 때 몸을 회복시키는 반응은 매우 빠른 다단계 시스템이다.

먼저 내이(inner ear)의 전정기관(vestibular apparatus)이 머리의 회전과 선형가속도를 감지한다.

신호는 전정신경을 통해 뇌간의 전정핵(vestibular nuclei)으로 전달된다.

전정척수로(vestibulospinal tract)와 망상척수로(reticulospinal tract)를 통해 자세근육의 긴장이 신속하게 조절된다.

소뇌는 몸의 실제 움직임과 예상 움직임을 비교한다.

척수 수준에서도 빠른 자세반응이 일어난다.

그보다 느린 시간대에는 대뇌피질과 기저핵이 개입하여 발을 내딛거나 손을 뻗는 것처럼 상황에 맞는 회복 동작을 선택한다.

즉 균형회복은

전정계

뇌간

척수

소뇌

기저핵

두정-전두 감각운동 네트워크


가 서로 다른 시간척도에서 협력하는 현상이다.


---

전두 안구영역: 눈의 방향은 주의의 방향과 연결된다

전두엽에는 전두 안구영역(frontal eye field, FEF)이 존재한다.

고전적으로 BA8과 관련지어 설명해왔지만 인간에서 실제 FEF의 해부학적 위치는 브로드만 경계와 정확하게 일치하지 않는다.

FEF는 자발적인 도약안구운동(saccade)과 시각적 주의에 중요한 역할을 한다.

교실에서 누군가

“오른쪽 위 그림을 보세요”

라고 말했을 때 시선을 그쪽으로 옮기는 것은 단순한 안구근육 반사가 아니다.

두정엽의 주의 네트워크가 중요한 위치를 선택하고,

전두 안구영역과 상구(superior colliculus)를 포함한 안구운동계가 실제 시선 이동을 조직한다.

반면 갑자기 옆에서 무언가 움직였을 때 자동으로 시선이 돌아가는 반응에는 상구와 뇌간의 빠른 회로가 더 강하게 관여한다.

의도적 시선 이동과 반사적 시선 이동을 구분해야 하는 이유가 여기에 있다.


---

인간 사고의 핵심은 단순한 시퀀스가 아니라 구조화된 시퀀스다

이제 강의를 관통하는 핵심 주제로 돌아갈 수 있다.

시퀀스는 분명 인간 지능의 중요한 요소다.

말도 순서가 있고,

운동도 순서가 있고,

사건 기억도 순서가 있으며,

계획 역시 시간적으로 배열된다.

하지만 더 본질적인 차원에서 인간의 고등사고를 설명하려면 “시퀀스”라는 말에 한 가지를 더 붙여야 한다.

구조화된 시퀀스(structured sequence)다.

인간은 단순히

A 다음 B,

B 다음 C

를 기억하는 것이 아니다.

A가 B의 원인이고,

B는 C의 조건이며,

D가 끼어들면 B 대신 E를 해야 한다

같은 관계 구조를 구성한다.

즉 인간 지능은 시퀀스 안에 관계와 조건과 계층을 삽입한다.


---

행동 시퀀스에도 계층이 있다

커피를 만드는 행동을 생각해 보자.

물을 끓인다.

컵을 준비한다.

커피를 넣는다.

물을 붓는다.

마신다.

이것은 단순한 다섯 단계의 배열이 아니다.

“커피 만들기”라는 상위 목표 아래 여러 하위 행동이 묶여 있다.

물을 끓이는 과정 자체도

주전자에 물을 넣는다

→ 전원을 켠다

→ 끓기를 기다린다

라는 더 작은 시퀀스로 이루어진다.

즉 행동은 계층적인 트리 구조를 가진다.

상위 목표

→ 하위 목표

→ 실제 운동

으로 내려간다.

인간이 복잡한 도구를 만들고 건축하고 과학실험을 수행할 수 있는 이유도 단순히 긴 시퀀스를 외울 수 있어서가 아니다.

수백 개의 행동을 여러 수준의 하위 목표로 묶어 관리할 수 있기 때문이다.


---

사건 기억 역시 시간구조를 가진다

해마는 단순히 장면을 저장하는 것이 아니다.

시간적으로 떨어진 사건들을 관계화한다.

아침에 일어났다.

학교에 갔다.

친구를 만났다.

수업을 들었다.

집으로 돌아왔다.

이러한 사건이 하나의 하루라는 시간적 구조 안에서 묶인다.

해마와 전전두피질의 상호작용은 과거 사건을 재구성하고, 그 구조를 이용해 미래 상황을 상상하는 데 중요하다.

그래서 기억과 미래계획은 완전히 별개의 기능이 아니다.

과거 경험을 구성하는 신경계가 미래 상황을 시뮬레이션할 때도 상당 부분 동원된다.


---

뇌의 전압 펄스란 정확히 무엇인가

강의에서 말한 전압 펄스는 신경과학적으로 활동전위(action potential)를 가리킨다고 볼 수 있다.

활동전위는 실제로 세포막 전압이 매우 빠르게 변하는 전기적 사건이다.

그러나 이 현상을 제대로 이해하려면 전기와 화학을 함께 보아야 한다.

뉴런 내부와 외부에는 이온 농도가 서로 다르다.

특히 나트륨 이온 Na⁺와 칼륨 이온 K⁺의 농도 차이가 중요하다.

세포막은 지질 이중층으로 이루어져 있기 때문에 이온이 마음대로 통과할 수 없다.

이온통로와 펌프가 이동을 조절한다.

이 농도차와 선택적 투과성 때문에 뉴런 내부에는 약한 음전위가 형성된다.

이를 휴지막전위(resting membrane potential)라고 한다.


---

다른 뉴런의 신호가 들어오는 순간

앞 뉴런의 축삭말단에서 신경전달물질이 방출된다.

신경전달물질은 다음 뉴런의 수용체에 결합한다.

그 결과 특정 이온통로가 열린다.

Na⁺가 들어오거나,

Cl⁻가 들어가거나,

K⁺의 이동이 달라지면서

세포막 전위가 조금 변한다.

이 변화는 활동전위와 달리 연속적인 크기를 가질 수 있다.

이를 시냅스후전위(postsynaptic potential)라고 한다.

한 뉴런은 이런 입력을 수천 개 이상 받을 수 있다.


---

뉴런은 입력의 시간과 공간을 합산한다

여기서 시퀀스 개념이 다시 등장한다.

뉴런은 단순히 “신호가 왔다/안 왔다”만 보는 것이 아니다.

어떤 입력이

언제 들어왔는가,

몇 개가 거의 동시에 들어왔는가,

수상돌기의 어느 위치에서 들어왔는가

가 중요하다.

여러 흥분성 입력이 짧은 시간 안에 겹치면 막전위가 임계값에 가까워진다.

억제성 입력이 들어오면 반대로 활동전위를 만들기 어려워진다.

즉 하나의 뉴런 자체가 이미 시간적·공간적 신호 통합 장치다.


---

임계값을 넘으면 활동전위가 발생한다

축삭 초기분절(axon initial segment)의 막전위가 임계값을 넘으면 전압개폐성 나트륨 통로가 빠르게 열린다.

Na⁺가 세포 안으로 유입되면서 막전위가 급격히 상승한다.

탈분극(depolarization)이다.

곧이어 나트륨 통로는 불활성화되고 전압개폐성 칼륨 통로가 열린다.

K⁺가 세포 밖으로 빠져나가면서 막전위가 다시 내려간다.

재분극(repolarization)이다.

이후 짧은 과분극과 불응기(refractory period)가 나타난다.

이 과정이 하나의 전기적 펄스를 만든다.


---

수초는 속도만 높이는 것이 아니다

축삭을 둘러싼 수초(myelin)는 전기적 절연체 역할을 한다.

수초가 있는 구간에서는 이온 흐름이 크게 줄어들고, 활동전위는 랑비에 결절(Node of Ranvier)에서 주로 재생된다.

이를 도약전도(saltatory conduction)라고 한다.

그래서 긴 축삭에서도 빠르고 에너지 효율적인 정보전달이 가능하다.

그러나 여기서 “신경전달 속도가 빠를수록 지능이 높다”고 결론 내리면 안 된다.

고등 뇌기능에서 중요한 것은 절대적인 속도뿐 아니라 서로 다른 뇌 영역의 신호가 적절한 시간관계로 도착하는 것이다.

몇 밀리초의 지연도 네트워크 동기화에는 의미가 있다.

뇌는 단순히 빠른 회로가 아니라 정교하게 시간 조율된 회로다.


---

전기신호는 다시 화학신호로 변환된다

활동전위가 축삭말단에 도달하면 전압개폐성 칼슘 통로가 열린다.

Ca²⁺가 세포 안으로 들어온다.

칼슘 유입은 시냅스 소포가 세포막과 융합하도록 유도한다.

소포 안의 신경전달물질이 시냅스 틈으로 방출된다.

그 물질이 다음 뉴런의 수용체에 결합하면 다시 막전위가 변한다.

따라서 신경회로에서는

전기적 신호

→ 화학적 신호

→ 전기적 신호

가 끊임없이 반복된다.

뇌를 순수한 전기회로라고 부를 수 없는 이유다.


---

생각은 뉴런 하나 안에 들어 있지 않다

이 지점에서 가장 중요한 원리를 이해할 수 있다.

생각 하나가 특정 뉴런 안에 들어 있는 것이 아니다.

기억 하나가 특정 브로드만 영역에 파일처럼 저장되는 것도 아니다.

인지적 내용은 다수의 뉴런 집단이 만드는 활동패턴과 연결구조에서 나타난다.

어떤 뉴런들이 함께 활성화되는지,

얼마나 강하게 활성화되는지,

어떤 순서로 활성화되는지,

서로 어떤 시냅스 연결을 가지고 있는지

가 모두 중요하다.

따라서 정신현상은 단순한 전압 펄스의 “순서”보다 더 복잡하다.

발화율(firing rate),

정확한 발화시점(spike timing),

뉴런 집단의 공간적 패턴(population code),

뇌 진동의 위상,

시냅스 강도,

도파민·아세틸콜린 같은 신경조절물질

등이 함께 관여한다.


---

학습한다는 것은 뇌의 연결확률을 바꾸는 일이다

지식을 배운다는 것을 컴퓨터 파일을 저장하는 것처럼 생각하면 실제 뇌와 거리가 멀다.

학습이 반복되면 뉴런 사이의 시냅스 효율이 변한다.

일부 연결은 강해지고,

일부는 약해지고,

새로운 연결이 형성되거나 기존 연결이 재구성된다.

장기강화(long-term potentiation, LTP)와 장기억제(long-term depression, LTD)가 대표적인 시냅스 가소성 기전이다.

따라서 학습의 본질은

“내용을 저장한다”

라기보다

“앞으로 어떤 신경상태가 어떤 다른 신경상태를 쉽게 불러오게 만들 것인가를 바꾼다”

에 가깝다.


---

구조화된 지식이 기억에 강한 이유

서로 관계없는 사실 백 개를 외우는 것과 하나의 인과 구조 안에 백 개의 사실을 묶는 것은 신경인지적으로 전혀 다르다.

예를 들어 다음 네 문장을 각각 외운다고 하자.

수초가 존재한다.

이온의 누설이 줄어든다.

랑비에 결절에서 활동전위가 재생된다.

전도가 빨라진다.

이 네 문장을 독립적으로 외우는 것보다

수초 형성

→ 막을 통한 전류 누설 감소

→ 결절 중심의 활동전위 재생

→ 도약전도

→ 빠르고 효율적인 축삭 전도

라는 인과사슬로 이해하면 하나의 요소가 나머지 요소를 불러오는 검색 단서가 된다.

지식이 구조화되었다는 것은 단순히 보기 좋게 정리했다는 뜻이 아니다.

한 개념에서 다른 개념으로 도달할 수 있는 경로가 많아졌다는 뜻이다.


---

글쓰기가 사고를 강화하는 이유

글쓰기를 단순히 전두엽 훈련이라고 설명할 필요는 없다.

글쓰기는 여러 인지 기능을 동시에 요구한다.

기억에서 관련 내용을 꺼내야 한다.

무엇이 중요한지 선택해야 한다.

앞 문장의 내용을 작업기억에 유지해야 한다.

다음 문장과 논리적으로 연결해야 한다.

단어를 선택하고 문법을 구성해야 한다.

전체 구조가 목적에 맞는지 다시 검토해야 한다.

잘못된 부분을 억제하고 수정해야 한다.

따라서 글쓰기는 기억계, 언어계, 전두-두정 인지통제계, 시각계, 운동계가 동시에 참여하는 고차원적 작업이다.

그리고 하나의 중요한 장점이 더 있다.

머릿속의 작업기억은 매우 제한적이다.

글은 생각을 외부에 고정한다.

이미 적어놓은 내용을 다시 바라보고 비교할 수 있다.

종이와 화면이 인간의 외부 작업기억(external working memory)이 되는 셈이다.

글쓰기가 사고를 정교하게 만드는 이유는 단순히 “문장을 쓰기 때문”이 아니라 머릿속에서 동시에 유지해야 할 정보의 일부를 환경으로 옮겨 놓기 때문이다.


---

이 강의를 공부할 때 가장 유용한 학습 방식

강의에서 말한 ‘구성적 사고’를 실제 학습으로 옮기려면 내용을 순서대로 외우는 것만으로는 부족하다.

예를 들어

“일차운동피질은 움직임을 담당한다”

를 암기하는 대신 다음 흐름을 재구성해야 한다.

목표 설정

→ 행동 선택

→ 운동계획

→ 전운동피질·보조운동영역

→ 일차운동피질

→ 뇌간·척수

→ 근육

→ 감각 피드백

→ 소뇌의 오차 수정

이렇게 공부하면 하나의 뇌 부위가 전체 시스템에서 어느 위치에 놓이는지 이해된다.

그 다음에는 흐름을 반대로 추론해보는 것이 좋다.

“손가락 움직임이 부정확하다면 어느 단계의 문제가 가능할까?”

“근력이 정상인데 운동 시퀀스가 잘 조직되지 않는다면 어디를 의심할 수 있을까?”

“시각은 정상인데 손을 정확히 물체 방향으로 뻗지 못한다면 어떤 좌표변환 과정이 손상되었을까?”

이런 질문을 통해 지식이 단순 암기에서 모델로 바뀐다.


---

주요 뇌 영역을 해부학적 이름 중심으로 다시 정리하면

해부학적 영역 기능적 의미 브로드만 번호는 참고용

일차시각피질 V1 초기 시각정보 분석 BA17
초기·고차 시각연합피질 형태·색·움직임 등 고차 시각처리 BA18·19과 부분적으로 대응
상두정소엽·후두정피질 시각-체성감각 통합, 공간 좌표변환, reaching BA7 일부
일차청각피질 기본적 청각 특징 분석 BA41 중심
청각연합피질 복합 청각패턴과 음성정보 분석 BA42 및 주변
후방 상측두회 언어적 청각정보와 의미 처리 네트워크의 일부 후방 BA22 포함
일차운동피질 수의운동 출력의 핵심 BA4
전운동피질·보조운동영역 운동준비, 순서화, 운동학습 BA6
전두 안구영역 자발적 시선 이동과 시각주의 BA8/6 경계 주변과 관련
하전두회 pars opercularis 음운·조음·구문 처리 등에 관여 BA44
하전두회 pars triangularis 의미선택·어휘·언어 통제 등에 관여 BA45
배외측 전전두피질 작업기억, 규칙 유지, 인지통제, 문제해결 BA9·46 상당 부분
전두극피질 미래 목표, 관계통합, 다중과제 BA10 상당 부분
해마 일화기억, 공간관계, 사건구조 브로드만 번호 체계와 별도로 이해해야 함
내후각피질 해마 입력·출력, 공간 좌표계 BA28·34와 관련
후방비장피질 방향, 랜드마크, 공간좌표 변환 BA29·30
기저핵 행동 선택, 습관화, 행동 시퀀스 학습 대뇌피질이 아니므로 BA 번호 없음
소뇌 예측, 운동오차 수정, 운동학습 대뇌피질의 BA 체계 밖


이런 식으로 쓰는 것이 그림 없이 읽을 때도 훨씬 낫습니다.

브로드만 번호는 “주소”이고, 실제로 기억해야 할 것은 해부학적 위치와 그 부위가 참여하는 계산입니다.


---

인간 지능을 시퀀스 관점에서 다시 정의하면

강의의 가장 중요한 통찰은 버릴 필요가 없다.

오히려 조금 더 엄밀하게 확장하면 상당히 좋은 사고 틀이 된다.

인간 뇌는 시간적으로 변화하는 정보를 처리한다.

소리는 시간에 따라 변한다.

운동은 시간에 따라 펼쳐진다.

문장은 시간적으로 발화된다.

사건은 순서대로 경험된다.

계획은 미래의 상태를 시간적으로 배열한다.

그러므로 시퀀스는 인간 뇌의 여러 기능을 관통한다.

하지만 인간 지능의 핵심을 단순한 “순서 기억 능력”으로 정의해서는 부족하다.

보다 정확하게는

시간적으로 전개되는 요소를 관계와 계층을 가진 구조로 묶고, 현재 목적에 맞게 그 구조를 유지·변형·재배열하며, 그 결과를 이용해 미래 상태를 예측하고 행동을 조직하는 능력

이라고 보는 편이 적절하다.

여기에 해마의 기억,

전두-두정 네트워크의 작업기억과 인지통제,

기저핵의 행동선택,

소뇌의 예측과 오차수정,

언어 네트워크의 계층적 구조처리가 결합한다.

그래서 인간은 단순히 긴 시퀀스를 실행하는 동물이 아니라

시퀀스 자체를 대상으로 사고하고,

시퀀스를 다른 시퀀스로 변환하고,

여러 시퀀스를 상위 목표 아래 묶으며,

아직 일어나지 않은 시퀀스를 미리 시뮬레이션할 수 있는 생물이라고 보는 편이 정확하다.

이것이 언어, 계획, 도구 제작, 글쓰기, 수학, 과학적 추론이 서로 완전히 다른 능력처럼 보이면서도 깊은 수준에서 공통된 신경계 원리를 공유하는 이유다.

하네스 엔지니어링: 바이브코딩을 프로덕션 엔지니어링으로 바꾸는 방법


1. 하네스 엔지니어링이란 무엇인가

하네스 엔지니어링(Harness Engineering)은 AI 에이전트(agent)가 실제 업무를 수행할 때, 모델 바깥의 실행 환경·도구·권한·컨텍스트·검증·관측성·승인 절차를 설계하는 공학이다.
간단히 말하면 이렇다.
모델이 “두뇌”라면, 하네스는 그 두뇌가 실제 개발 환경에서 안전하게 움직이도록 붙여주는 “작업 장비, 안전벨트, 계기판, 규칙, 검증 장치”다.Martin Fowler는 하네스를 “모델을 제외한 에이전트의 모든 것”에 가깝게 설명한다. 즉 에이전트는 단순히 LLM 하나가 아니라, Model + Harness로 구성된 시스템이다. 하네스에는 프롬프트, 컨텍스트, 도구, 메모리, 테스트, 피드백 루프, 권한 제어, 사람 개입 지점이 포함된다.
OpenAI도 Codex 기반 작업 경험을 설명하면서, 엔지니어의 역할이 단순히 코드를 직접 작성하는 것에서 에이전트가 일할 수 있는 환경과 제약을 설계하는 것으로 확장된다고 설명한다. 이 관점에서 하네스 엔지니어링은 AI 시대의 새로운 소프트웨어 공학 층이다.

2. 왜 하네스가 필요한가

바이브코딩(vibe coding)은 빠르다. 사용자가 “이런 기능 만들어줘”라고 말하면 AI가 파일을 만들고, 코드를 수정하고, 테스트까지 작성한다. 프로토타입에서는 매우 강력하다.
문제는 실제 서비스다.
AI는 보통 다음을 잘한다.
```
- 반복적인 코드 생성
- boilerplate 작성
- 익숙한 프레임워크 패턴 적용
- 테스트 초안 작성
- 작은 버그 수정
- 문서 기반 코드 수정
```
하지만 다음에는 취약하다.
```
- 인증/인가 보안 경계
- 결제 로직
- 개인정보 처리
- 운영 DB 접근
- 장기 유지보수성
- 사내 아키텍처 규칙
- 과거 장애 이력
- 도메인별 암묵적 예외
- 배포 후 실패 비용
```
즉 바이브코딩의 위험은 “AI가 코드를 못 짠다”가 아니다. 더 정확히는 AI가 너무 쉽게 그럴듯한 코드를 만들기 때문에, 사람이 검증하기 전에 위험한 변경이 들어갈 수 있다는 점이다.
하네스 엔지니어링은 이 문제를 이렇게 바꾼다.
```
나쁜 방식:
AI에게 “보안 신경 써서 만들어줘”라고 말한다.

좋은 방식:
AI가 보안 규칙을 위반하면 PR 자체가 생성되지 않게 만든다.
```
프롬프트는 조언이다.하네스는 제도와 인프라다.

3. 기존 에이전트 시스템과 무엇이 다른가

기존 에이전트 시스템은 보통 이런 구조다.
```
사용자 목표
   ↓
LLM이 계획
   ↓
도구 호출
   ↓
결과 관찰
   ↓
다시 계획
   ↓
완료
```
이건 agent loop, 즉 에이전트 실행 루프다.
하네스 엔지니어링은 그 바깥을 설계한다.
```
이 에이전트가 어떤 파일을 읽을 수 있는가?
어떤 파일을 쓸 수 있는가?
어떤 명령은 실행할 수 없는가?
운영 DB에는 접근할 수 없는가?
테스트 실패 시 어떻게 재시도하는가?
보안 스캔 실패 시 누가 승인하는가?
PR에는 어떤 증거를 남겨야 하는가?
나중에 실패 원인을 추적할 수 있는가?
```
즉 기존 agent framework가 “AI가 행동하는 루프”를 만드는 데 집중했다면, 하네스 엔지니어링은 그 행동 루프가 안전하고, 검증 가능하고, 감사 가능하고, 조직의 품질 기준을 따르도록 만드는 구조에 집중한다.
2026년 arXiv에 올라온 하네스 엔지니어링 관련 프리프린트도 소프트웨어 에이전트의 능력을 모델 단독이 아니라 model-harness-environment system, 즉 모델·하네스·환경의 결합으로 봐야 한다고 설명한다. 해당 논문은 task specification, context selection, tool access, memory, observability, verification, permissions 등을 하네스의 책임으로 정리한다. 다만 프리프린트이므로 동료심사를 거친 확정 이론으로 보기보다는 현재 연구 흐름으로 읽는 편이 안전하다.

4. 하네스 엔지니어링의 핵심 구성요소

실무에서 하네스는 보통 다음 층으로 나뉜다.
```
1. 작업 명세 계층
2. 위험도 분류 계층
3. 컨텍스트 수집 계층
4. 에이전트 런타임 계층
5. 도구 접근 계층
6. 샌드박스 실행 계층
7. 검증 게이트 계층
8. 사람 승인 계층
9. 관측성·로그 계층
10. CI/CD 운영 계층
```
전체 구조는 이렇게 볼 수 있다.
```
사용자 요청 또는 GitHub Issue
   ↓
작업 위험도 분류
   ↓
관련 코드·문서·이슈·정책 검색
   ↓
에이전트 실행
   ↓
허용된 도구만 호출
   ↓
샌드박스 안에서 코드 수정
   ↓
테스트·타입체크·린트·보안 스캔
   ↓
위험 작업이면 사람 승인
   ↓
PR 생성
   ↓
CI에서 다시 검증
   ↓
로그와 trace 저장
```
이 구조의 핵심은 모델이 자유롭게 움직이게 두는 것이 아니라, 좁고 명확한 통로를 통해 움직이게 만드는 것이다.

5. 실제 실무 스택 조합

하네스 엔지니어링은 특정 라이브러리 하나로 끝나지 않는다. 여러 도구를 목적별로 조합한다.

5.1 에이전트 런타임

대표 선택지는 다음이다.
```
- OpenAI Agents SDK
- LangGraph
- Pydantic AI
- LlamaIndex Workflows
- OpenHands SDK
```
OpenAI Agents SDK는 Agent와 Runner를 통해 turns, tool execution, guardrails, handoffs, sessions를 관리하는 구조다. 즉 단순 API 호출 래퍼가 아니라, 도구 호출형 에이전트 실행 루프를 관리하는 런타임이다.
LangGraph는 durable execution, streaming, human-in-the-loop 같은 장기 실행 에이전트 오케스트레이션 기능에 초점을 둔다. 중간 승인, 실패 후 재시작, 상태 기반 워크플로우가 중요한 경우 적합하다.

5.2 도구 연결 계층

최근에는 MCP(Model Context Protocol, 모델 컨텍스트 프로토콜)가 많이 언급된다. MCP는 AI 애플리케이션이 파일, DB, 검색, 계산기, 업무 시스템 같은 외부 시스템에 연결될 수 있게 하는 오픈 표준이다. 공식 문서는 MCP를 AI 애플리케이션을 외부 데이터·도구·워크플로우에 연결하는 표준으로 설명한다.
하지만 MCP를 붙였다고 자동으로 안전해지는 건 아니다. 하네스에서는 MCP tool마다 다음 제약을 붙여야 한다.
```
- 호출 가능한 tool allowlist
- 위험 tool denylist
- path validation
- argument schema validation
- rate limit
- approval policy
- audit log
```
즉 MCP는 연결 표준이고, 하네스는 그 위의 통제 체계다.

5.3 샌드박스 계층

AI가 만든 코드는 격리된 환경에서 실행해야 한다.
대표 도구는 다음이다.
```
- Docker
- devcontainer
- E2B
- Kubernetes isolated runner
- Firecracker 계열 microVM
- GitHub Actions ephemeral runner
```
E2B는 AI-generated code를 클라우드의 secure isolated sandbox에서 실행할 수 있는 오픈소스 인프라로 설명된다.  Hugging Face의 secure code execution 문서도 LLM 생성 코드를 로컬 환경에서 실행하는 데는 본질적 위험이 있으며, 더 강한 격리를 위해 E2B나 Docker 같은 remote execution 또는 sandbox 접근이 필요하다고 설명한다.

5.4 검증 게이트

하네스에서 가장 중요한 원칙은 이것이다.
모델의 자기평가가 아니라, 외부 검증 시스템의 결과를 믿는다.보통 다음 도구를 사용한다.
```
테스트:
- pytest
- Jest
- Vitest
- Playwright
- Cypress

타입체크:
- mypy
- pyright
- TypeScript tsc

린트:
- ruff
- eslint
- biome

보안:
- Semgrep
- CodeQL
- Snyk
- secret scanning
- dependency scan
```
Semgrep은 CI와 PR 스캔에 통합했을 때 pull request에서 새로 도입된 이슈를 보고하도록 사용할 수 있다.  GitHub CodeQL은 코드의 취약점과 오류를 찾아 GitHub code scanning alert로 표시하는 도구다.

5.5 관측성 계층

운영 환경에서는 “AI가 왜 그렇게 했는지”를 추적할 수 있어야 한다.
대표 도구는 다음이다.
```
- Langfuse
- LangSmith
- OpenTelemetry
- Arize Phoenix
- custom audit log
```
OpenAI Agents SDK는 LLM generation, tool call, handoff, guardrail, custom event를 포함한 tracing을 제공한다.  Langfuse는 OpenAI Agents workflow를 monitor, debug, evaluate하기 위한 통합을 제공한다.
관측성 없이 에이전트를 운영하면 나중에 이런 질문에 답하기 어렵다.
```
- AI가 어떤 파일을 읽고 수정했는가?
- 어떤 테스트가 실패했는가?
- 실패 후 무엇을 바꿨는가?
- 어떤 tool call이 위험했는가?
- 같은 실수가 반복되고 있는가?
- 비용이 어디서 폭주했는가?
```

6. 실무 적용 패턴


6.1 개인 개발자용 최소 하네스

개인 개발자라면 복잡한 플랫폼을 만들 필요는 없다. 최소한 이 정도만 해도 안전성이 크게 올라간다.
```
- AI가 수정할 branch를 따로 만든다.
- repo 전체를 무제한으로 맡기지 않는다.
- 수정 가능한 파일 범위를 정한다.
- .env, secret, production DB 접근을 주지 않는다.
- 테스트가 없으면 먼저 테스트를 만들게 한다.
- pytest/npm test/tsc/eslint를 반드시 실행한다.
- git diff를 사람이 읽고 merge한다.
- GitHub Actions에서 다시 검증한다.
```
개인용 최소 스택:
```
AI coding tool:
- Cursor
- Claude Code
- Codex CLI
- Aider
- OpenHands

검증:
- pytest 또는 Jest
- mypy 또는 TypeScript
- ruff 또는 eslint
- Semgrep

운영:
- Git
- Docker
- GitHub Actions
```

6.2 스타트업 제품팀용 하네스

스타트업에서는 “AI가 issue를 보고 PR까지 생성”하는 구조가 적합하다.
```
입력:
- GitHub issue
- Linear ticket
- Jira ticket

에이전트:
- OpenAI Agents SDK
- Pydantic AI
- LangGraph

컨텍스트:
- 코드 검색
- 문서 검색
- 과거 PR
- 관련 이슈

실행:
- Docker
- E2B
- GitHub Actions runner

검증:
- unit test
- integration test
- type check
- lint
- Semgrep

출력:
- PR
- 변경 요약
- 검증 결과
- 위험도 보고
```
흐름은 다음과 같다.
```
1. issue에 ai-fix 라벨을 붙인다.
2. 하네스가 issue 내용을 읽는다.
3. 위험도 분류를 한다.
4. 관련 코드와 테스트를 검색한다.
5. 샌드박스에서 임시 브랜치를 만든다.
6. AI가 patch를 제안한다.
7. 테스트, 타입체크, 린트, 보안검사를 실행한다.
8. 통과하면 PR을 만든다.
9. 사람이 review하고 merge한다.
```

6.3 엔터프라이즈용 하네스

금융, 의료, 교육, B2B SaaS처럼 실패 비용이 큰 조직에서는 더 강한 구조가 필요하다.
```
Agent runtime:
- LangGraph
- OpenAI Agents SDK
- OpenHands SDK

Tool protocol:
- MCP
- internal tool gateway

Context:
- 사내 문서 RAG
- 코드 검색
- ADR
- 장애 이력
- 보안 정책
- CODEOWNERS

Sandbox:
- Kubernetes isolated runner
- E2B private deployment
- Firecracker 계열 microVM
- ephemeral CI runner

Validation:
- unit test
- integration test
- e2e test
- SAST
- SCA
- secret scanning
- policy-as-code

Approval:
- GitHub CODEOWNERS
- Slack approval
- Jira transition
- security review

Observability:
- OpenTelemetry
- Langfuse
- LangSmith
- audit database
```
엔터프라이즈에서는 AI에게 “더 많은 권한”을 주는 것이 아니라, 오히려 권한을 더 잘게 쪼개고, 승인 절차를 명확히 하는 것이 핵심이다.

7. 실제 구현 예시: 안전한 코딩 하네스

아래는 Python 기준의 최소 구현 예시다.목표는 “AI가 파일을 수정하되, 허용된 경로만 수정하고, 테스트와 린트를 실제로 실행하게 하는 구조”다.

7.1 프로젝트 구조

```
repo/
├─ app/
│  └─ calculator.py
├─ tests/
│  └─ test_calculator.py
├─ harness/
│  ├─ safe_paths.py
│  ├─ policy.py
│  ├─ tools.py
│  ├─ agent.py
│  └─ run_task.py
└─ requirements.txt
```

7.2 예제 대상 코드

```
# app/calculator.py

def divide(a: float, b: float) -> float:
    return a / b
```
요구사항은 다음이다.
```
b가 0이면 Python 기본 ZeroDivisionError를 그대로 노출하지 말고,
ValueError를 발생시켜라.
```
테스트는 다음처럼 둔다.
```
# tests/test_calculator.py

import pytest

from app.calculator import divide


def test_divide_normal_case():
    assert divide(10, 2) == 5


def test_divide_by_zero_raises_value_error():
    with pytest.raises(ValueError):
        divide(10, 0)
```

7.3 path 정책: repo 바깥 접근 차단

```
# harness/safe_paths.py

from pathlib import Path


REPO_ROOT = Path(__file__).resolve().parents[1]

ALLOWED_WRITE_DIRS = [
    REPO_ROOT / "app",
    REPO_ROOT / "tests",
]

DENIED_PARTS = {
    ".env",
    ".git",
    "secrets",
    "private_key",
    "id_rsa",
}


def resolve_repo_path(relative_path: str) -> Path:
    """
    사용자가 넘긴 상대 경로를 repo 내부의 실제 경로로 바꾼다.

    핵심:
    - ../../ 같은 path traversal을 막는다.
    - repo 바깥 파일 접근을 막는다.
    - secret 계열 파일 접근을 막는다.
    """
    target = (REPO_ROOT / relative_path).resolve()

    if not str(target).startswith(str(REPO_ROOT)):
        raise PermissionError(f"repo 바깥 경로 접근 차단: {relative_path}")

    lowered = str(target).lower()
    if any(part in lowered for part in DENIED_PARTS):
        raise PermissionError(f"민감 파일 접근 차단: {relative_path}")

    return target


def assert_can_write(relative_path: str) -> Path:
    """
    AI가 파일을 수정하기 전에 반드시 통과해야 하는 정책 검사.
    """
    target = resolve_repo_path(relative_path)

    allowed = any(str(target).startswith(str(base)) for base in ALLOWED_WRITE_DIRS)
    if not allowed:
        raise PermissionError(f"쓰기 허용 범위 밖 파일: {relative_path}")

    return target
```
이 코드가 중요한 이유는 단순하다.AI에게 “민감 파일 읽지 마”라고 말하는 것보다, 애초에 코드로 접근을 막는 것이 훨씬 강하다.

7.4 위험도 분류

```
# harness/policy.py

from dataclasses import dataclass
from typing import Literal


Risk = Literal["low", "medium", "high", "critical"]


@dataclass
class TaskPolicy:
    risk: Risk
    requires_human_approval: bool
    reason: str


HIGH_RISK_KEYWORDS = [
    "auth",
    "authentication",
    "authorization",
    "login",
    "password",
    "payment",
    "billing",
    "invoice",
    "personal data",
    "pii",
    "database migration",
    "production",
]


def classify_task(description: str) -> TaskPolicy:
    """
    단순한 룰 기반 위험도 분류기.

    실무에서는 여기에 LLM 분류기, CODEOWNERS,
    파일 소유권, 장애 이력, 보안 정책을 같이 넣는다.
    """
    text = description.lower()

    for keyword in HIGH_RISK_KEYWORDS:
        if keyword in text:
            return TaskPolicy(
                risk="high",
                requires_human_approval=True,
                reason=f"고위험 키워드 감지: {keyword}",
            )

    return TaskPolicy(
        risk="low",
        requires_human_approval=False,
        reason="일반 코드 수정 작업",
    )
```
이 계층의 목적은 “AI가 결제·인증·개인정보 코드를 아무 승인 없이 수정하는 상황”을 막는 것이다.

7.5 안전 도구 만들기

OpenAI Agents SDK는 Python 함수를 function tool로 노출할 수 있다. 이때 중요한 점은 AI에게 범용 shell을 주지 않는 것이다. 대신 테스트 실행, 파일 읽기, 파일 쓰기, 검색 같은 좁은 도구만 제공한다. OpenAI Agents SDK는 Python 함수와 docstring을 기반으로 tool schema를 구성하는 function tool 방식을 제공한다.
```
# harness/tools.py

import subprocess

from agents import function_tool

from harness.safe_paths import REPO_ROOT, resolve_repo_path, assert_can_write


def _run_command(args: list[str]) -> str:
    """
    shell 문자열을 받지 않고 list[str]만 받는다.

    shell=True를 쓰지 않는 이유:
    - 명령어 인젝션 위험을 줄인다.
    - AI가 '; rm -rf' 같은 문자열을 섞어도 셸 해석이 일어나지 않는다.
    """
    result = subprocess.run(
        args,
        cwd=REPO_ROOT,
        text=True,
        capture_output=True,
        timeout=60,
        shell=False,
    )

    return (
        f"$ {' '.join(args)}\n\n"
        f"[stdout]\n{result.stdout}\n\n"
        f"[stderr]\n{result.stderr}\n\n"
        f"[exit_code]\n{result.returncode}"
    )


@function_tool
def read_file(path: str) -> str:
    """
    repo 내부 파일을 읽는다.

    Args:
        path: repo root 기준 상대 경로. 예: app/calculator.py
    """
    target = resolve_repo_path(path)

    if not target.exists():
        return f"파일이 존재하지 않습니다: {path}"

    if target.is_dir():
        return f"디렉터리는 읽을 수 없습니다: {path}"

    return target.read_text(encoding="utf-8")


@function_tool
def write_file(path: str, content: str) -> str:
    """
    허용된 경로의 파일만 전체 덮어쓴다.

    Args:
        path: repo root 기준 상대 경로. 예: app/calculator.py
        content: 새 파일 내용
    """
    target = assert_can_write(path)

    target.parent.mkdir(parents=True, exist_ok=True)
    target.write_text(content, encoding="utf-8")

    return f"파일 저장 완료: {path}"


@function_tool
def search_code(query: str) -> str:
    """
    app, tests 디렉터리에서 문자열을 검색한다.

    Args:
        query: 찾을 문자열. 예: divide
    """
    return _run_command(["rg", "-n", query, "app", "tests"])


@function_tool
def run_pytest() -> str:
    """
    pytest를 실행한다.
    """
    return _run_command(["pytest", "-q"])


@function_tool
def run_mypy() -> str:
    """
    mypy 타입 체크를 실행한다.
    """
    return _run_command(["mypy", "app", "tests"])


@function_tool
def run_ruff() -> str:
    """
    ruff lint를 실행한다.
    """
    return _run_command(["ruff", "check", "app", "tests"])


@function_tool
def run_semgrep() -> str:
    """
    Semgrep 보안 스캔을 실행한다.
    """
    return _run_command(["semgrep", "scan", "--config", "auto", "app", "tests"])
```
이 코드의 본질은 다음이다.
```
AI에게 준 것:
- read_file
- write_file
- search_code
- run_pytest
- run_mypy
- run_ruff
- run_semgrep

AI에게 주지 않은 것:
- raw shell
- production DB 접근
- secret 접근
- 배포 권한
- repo 바깥 파일 접근
```
이게 하네스 엔지니어링의 핵심이다.AI가 할 수 있는 일을 늘리는 게 아니라, 할 수 있는 일을 안전한 형태로 재정의하는 것이다.

7.6 에이전트 정의

```
# harness/agent.py

from agents import Agent

from harness.tools import (
    read_file,
    write_file,
    search_code,
    run_pytest,
    run_mypy,
    run_ruff,
    run_semgrep,
)


coding_agent = Agent(
    name="SafeCodingAgent",
    instructions="""
너는 안전한 코딩 에이전트다.

반드시 다음 규칙을 따른다.

1. 코드를 수정하기 전에 관련 파일을 먼저 읽어라.
2. 파일 구조를 모르면 search_code 도구로 확인해라.
3. 수정은 write_file 도구로만 수행해라.
4. 수정 후 반드시 run_pytest, run_mypy, run_ruff를 실행해라.
5. 보안 관련 코드가 포함되면 run_semgrep도 실행해라.
6. 테스트가 실패하면 실패 로그를 읽고 수정한 뒤 다시 검증해라.
7. 최종 답변에는 다음을 포함해라.
   - 변경한 파일
   - 변경 이유
   - 실행한 검증 명령
   - 통과/실패 결과
   - 사람이 추가 검토해야 할 위험
""",
    tools=[
        read_file,
        write_file,
        search_code,
        run_pytest,
        run_mypy,
        run_ruff,
        run_semgrep,
    ],
)
```
여기서 프롬프트도 중요하지만, 더 중요한 것은 도구 설계다.프롬프트는 에이전트에게 방향을 준다.도구와 정책은 에이전트의 행동 가능 범위를 결정한다.

7.7 실행기

```
# harness/run_task.py

import sys
from dotenv import load_dotenv
from agents import Runner

from harness.agent import coding_agent
from harness.policy import classify_task


load_dotenv()


def main() -> None:
    if len(sys.argv) < 2:
        raise SystemExit(
            "사용법: python -m harness.run_task '작업 설명을 여기에 입력'"
        )

    task_description = sys.argv[1]
    policy = classify_task(task_description)

    print("=== Task Policy ===")
    print(f"risk: {policy.risk}")
    print(f"requires_human_approval: {policy.requires_human_approval}")
    print(f"reason: {policy.reason}")
    print()

    if policy.requires_human_approval:
        print("사람 승인 필요. 에이전트 실행을 중단합니다.")
        return

    prompt = f"""
작업:
{task_description}

허용 범위:
- app/ 아래 파일
- tests/ 아래 파일

완료 조건:
- pytest 통과
- mypy 통과
- ruff 통과
- 변경 요약과 검증 결과 보고

주의:
- 허용 범위 밖 파일은 수정하지 마라.
- 테스트 결과를 실제 도구 실행 결과로 확인하라.
"""

    result = Runner.run_sync(
        coding_agent,
        prompt,
        max_turns=12,
    )

    print("=== Agent Final Output ===")
    print(result.final_output)


if __name__ == "__main__":
    main()
```
실행 예시는 다음이다.
```
python -m harness.run_task \
  "app/calculator.py의 divide 함수가 0으로 나눌 때 ValueError를 발생시키도록 고쳐라"
```
이 작업에서 에이전트가 이상적으로 수행해야 하는 순서는 다음이다.
```
1. search_code("divide")
2. read_file("app/calculator.py")
3. read_file("tests/test_calculator.py")
4. write_file("app/calculator.py", 수정된 코드)
5. run_pytest()
6. run_mypy()
7. run_ruff()
8. 최종 보고
```
최종 코드 결과는 대략 이렇게 된다.
```
# app/calculator.py

def divide(a: float, b: float) -> float:
    if b == 0:
        raise ValueError("division by zero is not allowed")

    return a / b
```

8. CI에서 최종 검증하기

에이전트가 로컬 또는 샌드박스에서 “통과했다”고 해도, 최종 신뢰는 CI가 담당해야 한다.
```
# .github/workflows/ai-harness-check.yml

name: AI Harness Check

on:
  pull_request:
    branches: [main]

permissions:
  contents: read
  security-events: write

jobs:
  validate:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt

      - name: Run tests
        run: pytest -q

      - name: Run type check
        run: mypy app tests

      - name: Run lint
        run: ruff check app tests

      - name: Run Semgrep
        run: semgrep scan --config auto app tests
```
보안 강도를 더 높이려면 CodeQL도 추가한다.
```
# .github/workflows/codeql.yml

name: CodeQL

on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

permissions:
  security-events: write
  packages: read
  actions: read
  contents: read

jobs:
  analyze:
    name: CodeQL Analyze
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v3
        with:
          languages: python

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v3
```
CI의 의미는 단순 자동화가 아니다.하네스 관점에서 CI는 AI의 작업물을 조직의 품질 기준으로 다시 검증하는 최종 게이트다.

9. 샌드박스 적용

로컬에서 subprocess를 실행하는 구조는 간단하지만, 실제 서비스에서는 격리된 환경이 필요하다.
E2B를 쓰면 AI-generated code를 샌드박스에서 실행할 수 있다. E2B는 AI agent를 위한 secure computer와 sandbox runtime을 제공한다고 설명한다.
개념 코드는 다음과 같다.
```
# harness/e2b_sandbox_example.py

from dotenv import load_dotenv
from e2b_code_interpreter import Sandbox

load_dotenv()


def run_in_e2b(code: str) -> str:
    """
    E2B 샌드박스에서 코드를 실행한다.

    실무에서는 여기에 다음을 붙인다.
    - repo clone
    - dependency install
    - patch apply
    - pytest/mypy/ruff/semgrep 실행
    """
    sbx = Sandbox.create()

    execution = sbx.run_code(code)

    return str(execution)


if __name__ == "__main__":
    output = run_in_e2b(
        """
print("샌드박스 내부에서 실행 중")
print(1 + 2)
"""
    )

    print(output)
```
실제 운영 흐름은 다음에 가깝다.
```
1. GitHub issue 수신
2. 샌드박스 생성
3. repo clone
4. dependency install
5. AI가 patch 생성
6. patch 적용
7. 테스트·타입체크·린트·보안 스캔 실행
8. 통과하면 PR 생성
9. 실패하면 로그를 agent에게 다시 제공
10. 반복 횟수 초과 시 사람에게 넘김
```
샌드박스를 쓴다고 모든 문제가 해결되는 건 아니다. 반드시 다음을 지켜야 한다.
```
- production credential을 넣지 않는다.
- 실제 결제 API key를 넣지 않는다.
- 운영 DB에 연결하지 않는다.
- 외부 네트워크 접근을 제한한다.
- 실행 시간과 리소스를 제한한다.
- 모든 파일 변경과 tool call을 기록한다.
```

10. LangGraph로 확장하는 방식

OpenAI Agents SDK는 빠르게 도구 호출형 에이전트를 만들기 좋다.하지만 승인, 재시도, 장기 실행, 상태 저장이 많아지면 LangGraph 스타일이 더 적합하다.
LangGraph는 node와 edge로 workflow를 구성하는 방식이다. 각 node는 현재 state를 받아 처리하고, edge가 다음 단계를 결정한다.
하네스를 LangGraph로 설계하면 다음과 같다.
```
classify_task
   ↓
collect_context
   ↓
generate_patch
   ↓
apply_patch
   ↓
run_checks
   ↓
security_review
   ↓
human_approval?
   ↓
create_pr
```
간단한 골격은 다음과 같다.
```
from typing import TypedDict, Literal

from langgraph.graph import StateGraph, START, END


class HarnessState(TypedDict):
    task: str
    risk: Literal["low", "medium", "high", "critical"]
    context: str
    patch_summary: str
    check_output: str
    approved: bool


def classify_task_node(state: HarnessState) -> dict:
    task = state["task"].lower()

    if "payment" in task or "auth" in task or "database" in task:
        return {"risk": "high"}

    return {"risk": "low"}


def collect_context_node(state: HarnessState) -> dict:
    return {
        "context": "관련 파일과 테스트, 문서, 과거 PR을 수집했다는 가정"
    }


def generate_patch_node(state: HarnessState) -> dict:
    return {
        "patch_summary": "LLM이 생성한 patch 요약"
    }


def run_checks_node(state: HarnessState) -> dict:
    return {
        "check_output": "pytest passed\nmypy passed\nruff passed"
    }


def route_after_checks(state: HarnessState) -> str:
    if state["risk"] in {"high", "critical"}:
        return "wait_for_approval"

    return "create_pr"


def wait_for_approval_node(state: HarnessState) -> dict:
    return {"approved": False}


def create_pr_node(state: HarnessState) -> dict:
    print("PR 생성")
    print(state["patch_summary"])
    print(state["check_output"])
    return {}


builder = StateGraph(HarnessState)

builder.add_node("classify_task", classify_task_node)
builder.add_node("collect_context", collect_context_node)
builder.add_node("generate_patch", generate_patch_node)
builder.add_node("run_checks", run_checks_node)
builder.add_node("wait_for_approval", wait_for_approval_node)
builder.add_node("create_pr", create_pr_node)

builder.add_edge(START, "classify_task")
builder.add_edge("classify_task", "collect_context")
builder.add_edge("collect_context", "generate_patch")
builder.add_edge("generate_patch", "run_checks")

builder.add_conditional_edges(
    "run_checks",
    route_after_checks,
    {
        "wait_for_approval": "wait_for_approval",
        "create_pr": "create_pr",
    },
)

builder.add_edge("wait_for_approval", END)
builder.add_edge("create_pr", END)

graph = builder.compile()

result = graph.invoke(
    {
        "task": "app/calculator.py divide 버그 수정",
        "risk": "low",
        "context": "",
        "patch_summary": "",
        "check_output": "",
        "approved": False,
    }
)

print(result)
```
이 방식은 특히 다음 상황에서 유리하다.
```
- 작업 시간이 길다.
- 중간에 사람 승인이 필요하다.
- 실패 지점부터 재시작해야 한다.
- 여러 에이전트가 역할을 나눠야 한다.
- audit trail이 중요하다.
```

11. 하네스 설계 원칙


11.1 raw shell을 주지 말 것

나쁜 설계:
```
run_shell(command: str)
```
좋은 설계:
```
run_unit_tests()
run_typecheck()
run_linter()
run_security_scan()
read_file(path)
write_file(path, content)
```
범용 shell은 너무 강하다.하네스에서는 범용 능력을 좁은 안전 도구로 쪼갠다.

11.2 프롬프트보다 코드로 강제할 것

약한 방식:
```
운영 DB는 절대 건드리지 마.
```
강한 방식:
```
if target_env == "production":
    raise PermissionError("Agent cannot access production database")
```
AI에게 금지사항을 설명하는 것은 필요하지만 충분하지 않다.금지는 코드, 네트워크, 권한, CI policy로 강제해야 한다.

11.3 검증은 시스템이 수행할 것

나쁜 방식:
```
AI: 테스트는 통과할 것 같습니다.
```
좋은 방식:
```
CI:
- pytest passed
- mypy passed
- ruff passed
- semgrep passed
```
모델의 판단은 참고다.최종 근거는 검증 결과다.

11.4 위험도별 자동화 수준을 다르게 둘 것

```
문서 수정:
- 자동 PR 가능

UI 문구 수정:
- 자동 PR 가능

테스트 추가:
- 자동 PR 가능

일반 API 로직:
- PR + 사람 리뷰

인증/인가:
- 사람 승인 필수

결제:
- 사람 승인 필수

개인정보 처리:
- 사람 승인 필수

DB migration:
- 별도 승인 필수

운영 배포:
- 원칙적으로 사람 승인 필수
```
하네스 엔지니어링의 목표는 모든 것을 자동화하는 것이 아니다.자동화해도 되는 것과 사람이 반드시 봐야 하는 것을 나누는 것이 핵심이다.

12. 적용 순서

실제 팀에 도입한다면 다음 순서가 좋다.

1단계: 읽기 전용 에이전트

처음부터 코드 수정을 맡기지 않는다.
```
- 이슈 요약
- 관련 파일 찾기
- 영향 범위 분석
- 테스트 후보 제안
- 위험도 분류
```
이 단계에서는 에이전트가 코드를 쓰지 않는다.

2단계: 테스트 작성 에이전트

다음으로 테스트만 작성하게 한다.
```
- tests/ 아래만 수정 가능
- app/ 수정 금지
- 테스트 실패 로그 보고
```
테스트는 기능 요구사항을 고정하는 역할을 한다.AI가 구현 전에 테스트를 만들면, 이후 구현의 기준이 생긴다.

3단계: 저위험 코드 수정

다음은 낮은 위험도의 코드만 맡긴다.
```
- 문서
- UI copy
- 단순 유틸 함수
- 타입 오류 수정
- lint fix
- 테스트 보강
```
이 단계부터는 PR 생성까지 자동화할 수 있다.

4단계: 샌드박스와 CI 필수화

이 단계에서는 모든 AI 실행을 격리한다.
```
- Docker 또는 E2B
- production secret 미주입
- mock DB 사용
- timeout 설정
- resource limit 설정
- CI 재검증
```

5단계: 고위험 도메인 승인 체계

마지막으로 고위험 도메인을 명확히 분리한다.
```
- auth
- payment
- user data
- database migration
- deployment
- infrastructure
```
이 영역은 AI가 patch를 제안할 수는 있어도, 사람 승인 없이 반영하면 안 된다.

13. 하네스 엔지니어링의 본질

하네스 엔지니어링은 “프롬프트를 잘 쓰는 법”이 아니다.또 “AI 코딩 툴을 하나 도입하는 법”도 아니다.
더 본질적으로는 다음이다.
AI가 실수할 것을 전제로, 그 실수가 시스템으로 들어가기 전에 막고, 발견하고, 되돌리고, 기록하게 만드는 소프트웨어 공학이다.기존 바이브코딩은 빠르지만, 검증과 통제 장치가 약하면 위험하다.하네스 엔지니어링은 그 속도를 버리지 않으면서, 다음을 추가한다.
```
- 허용 범위
- 금지 범위
- 컨텍스트 공급
- 샌드박스 실행
- 테스트 검증
- 보안 스캔
- 사람 승인
- 감사 로그
- 실패 재현성
```
그래서 한 문장으로 정리하면 다음과 같다.
하네스 엔지니어링은 AI 에이전트를 “그럴듯한 코드를 만드는 도구”에서 “검증 가능한 소프트웨어 생산 과정에 참여하는 통제된 작업자”로 바꾸는 공학이다.앞으로의 개발자는 AI에게 단순히 “코드 짜줘”라고 말하는 사람이 아니라,AI가 안전하게 일할 수 있는 작업장, 도구, 규칙, 검증 체계를 설계하는 사람이 된다.
이 변화의 핵심은 모델이 아니다.모델은 계속 바뀐다. GPT, Claude, Gemini, Llama, Codex 계열은 계속 교체될 수 있다.
하지만 좋은 하네스는 남는다.
```
모델은 교체 가능해야 한다.
도구는 제한 가능해야 한다.
검증은 자동화되어야 한다.
권한은 최소화되어야 한다.
위험 작업은 승인되어야 한다.
모든 행동은 추적 가능해야 한다.
```
이 원칙이 지켜질 때, 바이브코딩은 단순한 감각적 자동완성이 아니라 실제 프로덕션 엔지니어링의 일부가 된다.

 

 

대부분의 Python 개발자들은 **비동기 작업(Background Task)**을 처리할 때 Celery를 떠올린다.

하지만 Celery는 설정이 복잡하고, 유지보수가 어렵고, 과한 기능이 많아 작은 프로젝트에서는 오히려 불편하다.

 

📌 RQ (Redis Queue)와 Dramatiq은 이런 문제를 해결하는 가볍고 강력한 대안이다.

특히 RQ는 간단한 Redis 기반 큐 시스템, Dramatiq은 Celery와 비슷하면서도 훨씬 직관적이고 빠른 대안이다.

1. RQ (Redis Queue): 초간단 Background Task 라이브러리

 

RQ는 Celery보다 훨씬 단순한 구조로, Redis만 있으면 즉시 사용 가능하다.

설치부터 사용까지 1분이면 충분하다.

 

🚀 RQ 설치 및 사용법

pip install rq
import time
from redis import Redis
from rq import Queue

# Redis 연결 및 큐 생성
redis_conn = Redis()
queue = Queue(connection=redis_conn)

# 비동기 실행할 함수 정의
def background_task(n):
    time.sleep(n)
    return f"완료: {n}초 후"

# 작업을 큐에 넣기
job = queue.enqueue(background_task, 5)

print(f"작업 ID: {job.id}")  # 작업 ID 출력

🔹 RQ Worker 실행 (작업 처리)

 

RQ는 Celery처럼 복잡한 설정 없이 worker 실행만으로 비동기 작업을 처리할 수 있다.

rq worker

결과:

RQ Worker가 실행되면서 대기 중인 작업을 즉시 처리한다.

Celery처럼 복잡한 설정 없이, 단순한 작업을 Redis에서 관리할 때 매우 유용하다.

2. Dramatiq: Celery를 완벽하게 대체할 강력한 백그라운드 태스크 라이브러리

 

Celery는 강력하지만, 설정이 너무 복잡하고 무겁다는 단점이 있다.

Dramatiq은 Celery와 거의 동일한 기능을 제공하지만 훨씬 가볍고 빠르다.

 

🚀 Dramatiq 설치 및 기본 사용법

pip install dramatiq redis
import dramatiq
import time

# 비동기 태스크 정의
@dramatiq.actor
def background_task(n):
    time.sleep(n)
    print(f"완료: {n}초 후")

# 태스크 실행
background_task.send(5)

🔹 Dramatiq Worker 실행

 

Celery처럼 복잡한 celeryconfig.py 설정 없이, 단순히 worker만 실행하면 된다.

dramatiq my_script

Dramatiq의 장점

Celery보다 설정이 간편하고,

Redis, RabbitMQ, Kafka 등 다양한 메시지 브로커를 지원,

멀티 프로세싱과 멀티스레딩 지원으로 성능이 뛰어나다.

📌 RQ vs Celery vs Dramatiq 비교

기능RQCeleryDramatiq

설치 난이도 매우 쉬움 복잡함 쉬움
메시지 브로커 Redis Redis, RabbitMQ, SQS Redis, RabbitMQ, Kafka
성능 가벼움 무거움 빠름
비동기 작업 지원 지원 지원
멀티 프로세스 지원 지원 지원 (최적화)

RQ는 간단한 작업 큐,

Dramatiq은 Celery를 대체할 강력한 옵션이다.

🚀 결론: 언제 어떤 걸 써야 할까?

 

RQ를 선택해야 할 때

Redis만 사용하고 싶을 때

단순한 백그라운드 태스크 큐가 필요할 때

빠르게 개발하고 싶을 때

 

Dramatiq을 선택해야 할 때

Celery의 기능이 필요하지만 더 가볍고 빠른 솔루션이 필요할 때

RabbitMQ, Kafka 등 다양한 브로커를 활용할 때

성능 최적화가 중요한 시스템에서 사용할 때

📌 Celery가 너무 무겁다면?

📌 RQ와 Dramatiq을 적극 고려해보자!

+ Recent posts