
n8n 학습기 시리즈 — 1편. 핵심 개념: 노드, 트리거, 그리고 JSON
0편에서 왜 n8n을 배우기로 했는지 적었다. 이번 편은 실제로 띄워보고 첫 워크플로우를 뜯어보며 개념을 정리한다.
말로만 개념을 읽는 것보다, 일단 화면을 띄워놓고 노드를 만져보는 게 빠르다고 생각했다. 그래서 이번 편은 순서가 이렇다. 먼저 n8n을 셀프호스팅으로 띄우고, 첫 워크플로우를 하나 만들어 실행해본 다음, 거기서 관찰한 걸로 핵심 개념 세 가지(노드 / 트리거 / 데이터)를 정리한다.
0편에서 말했듯 처음부터 셀프호스팅으로 가기로 했다. 클라우드 버전의 실행 수 제약 없이 구조를 제대로 이해하고 싶었고, Docker 컨테이너 하나 띄우는 건 부담도 아니니까.
Mac에 Docker Desktop이 깔려 있다는 전제로, 터미널에 다음 두 줄이면 끝난다.
docker volume create n8n_data
docker run -it --rm \
--name n8n \
-p 5678:5678 \
-v n8n_data:/home/node/.n8n \
docker.n8n.io/n8nio/n8n
첫 줄은 워크플로우와 자격 증명(Credentials)을 저장할 볼륨을 만드는 것이다. 이걸 붙여줘야 컨테이너를 내렸다 올려도 만든 워크플로우가 날아가지 않는다. 이미지 경로가 docker.n8n.io/n8nio/n8n인 점만 주의하면 된다(공식 레지스트리 경로다).

명령이 돌면 이미지를 받아 컨테이너가 뜨고, 브라우저에서 http://localhost:5678 로 접속하면 편집기가 나온다. 최초 1회 계정(오너) 설정을 하고 나면 빈 캔버스가 열린다.


참고: 최근 버전은
-e N8N_RUNNERS_ENABLED=true같은 환경 변수를 권장하는데, 입문 단계에선 위 최소 구성으로도 충분하다. 프로덕션으로 갈 땐 타임존, DB(PostgreSQL) 분리 등을 붙이게 되지만 그건 한참 뒤 이야기다.
빈 캔버스에서 첫인상은 단순했다. 자동화의 각 단계가 노드(Node) 라는 상자 하나이고, 노드를 선으로 이어 흐름을 만든다. 코드로 치면 함수 하나하나를 상자로 그려놓고, 앞 함수의 리턴값을 뒤 함수 입력으로 넘기는 걸 눈에 보이게 만든 셈이다.
노드는 크게 두 종류로 나뉜다.
이 구분이 코드와 대응이 잘 됐다. 트리거 노드는 이벤트 리스너나 진입점(main, 웹훅 핸들러, cron 잡)이고, 일반 노드는 그 안에서 호출되는 함수들이다.
트리거 종류를 훑어보니 익숙한 것들이었다.

결국 "무엇이 이 자동화를 깨우는가"를 고르는 것이고, 코드로 자동화할 때 내가 매번 직접 만들던 진입 조건(스케줄러 등록, 웹훅 엔드포인트 개설, 폴링 루프)을 플랫폼이 노드 하나로 대신 제공하는 구조였다. 0편에서 "매번 새로 만들던 뼈대를 플랫폼이 깔아준다"고 기대했던 게 이 지점에서 처음 체감됐다.
여기가 제일 중요하다. n8n을 이해하는 열쇠는 노드 사이를 흐르는 데이터의 모양을 아는 것이다.
n8n에서 노드 간 데이터는 항상 아이템(item)들의 배열로 흐른다. 그리고 각 아이템은 이런 형태다.
[
{
"json": {
"id": 1,
"title": "예시 데이터"
}
},
{
"json": {
"id": 2,
"title": "다음 아이템"
}
}
]
핵심을 정리하면 이렇다.
json 키 안에 들어 있다. (파일 같은 바이너리는 별도로 binary 키에 담긴다.)json을 거쳐 접근하게 된다. (구체적인 표현식 문법은 2편에서 다룬다.)처음엔 "왜 데이터를 굳이 배열로 감싸고, 또 json 키로 한 번 더 감싸지?" 싶었다. 그런데 이유는 금방 납득됐다. 자동화는 대부분 여러 건을 다룬다. 메일 10통, 행 100개, 파일 여러 개. 데이터를 항상 배열로 취급하면, 노드는 "한 건이든 여러 건이든 똑같은 방식으로" 처리할 수 있다. 코드로 치면 단일 객체와 리스트를 분기 처리하지 않고, 처음부터 전부 리스트로 두고 map을 도는 것과 같은 발상이다.
개념을 글로 읽었으니 실제로 확인해봤다. 가장 단순한 조합이다.
Trigger manually → HTTP Request
When clicking 'Execute workflow'로 표시된다.)GET, URL에 테스트용 공개 API를 넣는다.
GET https://jsonplaceholder.typicode.com/todos/1
실행하면 HTTP Request 노드에 결과가 뜨는데, 여기서 처음으로 "데이터가 흐른다"는 게 눈에 보인다. 노드 출력 패널을 보면 응답이 이렇게 담겨 있다.

[
{
"json": {
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
}
]
앞서 정리한 그대로다. API 응답이 아이템 배열의 json 키 안에 들어와 있다. n8n UI에서는 이 출력을 Table(표), JSON, Schema 세 가지 뷰로 볼 수 있는데, 특히 Schema 뷰가 다음 노드에서 어떤 필드를 참조할 수 있는지 보여줘서 유용했다.
여기까지가 1편의 목표였다. HTTP Request 한 줄로 외부 데이터를 가져오는 건 코드로 수백 번 해봤지만, 그 결과가 정해진 형태(아이템 배열 + json 키)로 캔버스 위를 흐르고, 다음 노드가 그걸 받아 쓸 수 있다는 감각이 n8n의 출발점이었다.
정리하면서 계속 코드와 겹쳐 봤다.
main() 호출)fetch / axios 한 줄map으로 순회하는 것여기까지 보면 "그냥 코드가 더 빠르겠는데?"라는 생각도 들었다. 실제로 단발성 스크립트라면 그 말이 맞다. 다만 n8n의 값어치는 이 다음부터 나온다고 본다. 인증을 노드가 관리하고, 스케줄과 웹훅이 트리거로 붙고, 실패 시 재시도가 얹히는 순간, 내가 매번 짜던 "주변 코드"가 사라진다. 그 대조를 다음 편들에서 계속 확인해볼 생각이다.
json 키 아래에 있다는 걸 놓치면, 나중에 표현식에서 필드를 잘못 참조하게 된다. 이건 2편에서 제대로 정리할 예정.1편의 결론은 한 문장이다. n8n에서 데이터는 아이템 배열의 형태로, 각 값은 json 키 안에 담겨 노드 사이를 흐른다. 이 구조만 손에 익으면 나머지는 그 위에 얹히는 이야기다.
다음 2편에서는 이 데이터를 실제로 꺼내 쓰고 변형하는 방법, 즉 표현식({{ $json }})과 노드 간 데이터 참조를 정리한다. 개인적으로 여기가 n8n에서 제일 헷갈리면서도 제일 중요한 부분이라 생각해서, 시간을 들여 다뤄보려 한다.
읽어주셔서 감사합니다. 🙌