G: 안녕하세요, 자동화와 효율성에 목마른 여러분! 🚀 n8n은 코딩 없이도 복잡한 자동화 워크플로우를 구축할 수 있게 해주는 강력한 도구입니다. 하지만 때로는 n8n의 기본 노드만으로는 해결하기 어려운, 더 정교하고 맞춤화된 로직이 필요할 때가 있죠. 바로 이때, n8n 워크플로우의 ‘마법 지팡이’이자 ‘비밀 병기’인 Code Node가 등장합니다! ✨
Code Node는 n8n 워크플로우에 직접 JavaScript 코드를 삽입하여 실행할 수 있게 해주는 노드입니다. 이를 통해 데이터 변환, 복잡한 조건부 로직, 외부 라이브러리 연동 등 무궁무진한 가능성을 열 수 있습니다.
오늘은 이 강력한 Code Node를 활용하여 여러분의 워크플로우를 한 단계 더 업그레이드할 수 있는 실전 활용 예시 10가지를 자세히 살펴보겠습니다. 지금부터 함께 데이터 마법의 세계로 떠나볼까요? 🧙♂️
🌟 n8n Code Node는 무엇인가요?
n8n Code Node는 n8n 워크플로우 내에서 JavaScript 코드를 실행할 수 있는 특별한 노드입니다. n8n의 모든 데이터는 JSON(JavaScript Object Notation)
형식으로 처리되기 때문에, JavaScript를 사용하여 데이터를 자유롭게 조작하고, 새로운 데이터를 생성하며, 복잡한 비즈니스 로직을 구현할 수 있습니다.
Code Node를 사용해야 하는 이유:
- 무한한 확장성: n8n에 내장된 노드만으로는 불가능한 거의 모든 작업을 구현할 수 있습니다.
- 맞춤형 데이터 처리: 데이터를 특정 형식으로 변환하거나, 여러 소스의 데이터를 조합하는 등 정교한 데이터 조작이 가능합니다.
- 복잡한 로직 구현: 단순한
IF
노드로는 처리하기 어려운 다중 조건, 반복 작업 등을 유연하게 처리할 수 있습니다. - 외부 라이브러리 연동 (고급): 필요에 따라
uuid
,lodash
같은 외부 npm 패키지를 설치하여 활용할 수도 있습니다 (n8n 서버 설정 필요). - 디버깅 및 로깅:
$node
객체를 통해 경고, 에러 메시지를 콘솔이나 n8n UI에 표시하여 디버깅에 도움을 받을 수 있습니다.
작동 방식:
Code Node는 입력으로 들어오는 데이터를 items
라는 배열 형태로 받습니다. 각 item
은 json
속성 안에 실제 데이터를 가지고 있습니다. 여러분은 이 items
배열을 JavaScript 코드로 조작한 후, 다시 items
배열 형태로 반환하면 됩니다.
- 입력 데이터 접근:
items[0].json.propertyName
- 출력 데이터 반환:
return items;
💡 n8n Code Node 실전 활용 예시 10가지
이제 Code Node의 강력함을 직접 느껴볼 시간입니다! 각 예시는 시나리오, 문제점, Code Node를 사용한 해결책, 그리고 간단한 코드 스니펫으로 구성됩니다.
1. 🔍 데이터 필터링 및 정제
가장 흔하게 사용되는 용도 중 하나입니다. 특정 조건을 만족하는 데이터만 걸러내거나, 불필요한 데이터를 제거할 때 유용합니다.
- 시나리오: 온라인 쇼핑몰에서 발생한 주문 중, 결제 금액이 50,000원을 초과하는 주문만 다음 단계로 처리하고 싶습니다.
- 문제점: 기본
IF
노드로는 단일 조건만 간단히 처리할 수 있지만, 복잡한 OR/AND 조건이나 동적인 필터링에는 한계가 있습니다. - Code Node 활용: JavaScript의
filter()
메서드를 사용하여 원하는 조건에 맞는item
만 걸러냅니다.
// 입력 데이터 예시:
// [
// { "json": { "orderId": "ORD001", "amount": 35000, "status": "completed" } },
// { "json": { "orderId": "ORD002", "amount": 62000, "status": "completed" } },
// { "json": { "orderId": "ORD003", "amount": 50000, "status": "pending" } }
// ]
const filteredItems = items.filter(item => {
// 금액이 50000원 초과 AND 상태가 'completed'인 주문만 필터링
return item.json.amount > 50000 && item.json.status === 'completed';
});
return filteredItems;
// 출력 데이터 예시 (filteredItems):
// [
// { "json": { "orderId": "ORD002", "amount": 62000, "status": "completed" } }
// ]
2. 🏗️ 데이터 구조 변경 (JSON Flattening/Reshaping)
복잡하게 중첩된 JSON 데이터를 평탄화하거나, 새로운 구조로 재편성해야 할 때 빛을 발합니다.
- 시나리오: 외부 API에서 받은 고객 데이터가
user: { address: { street: '...', city: '...' } }
와 같이 중첩되어 있습니다. 이를city
,street
와 같은 최상위 속성으로 변경하고 싶습니다. - 문제점:
Set
노드로는 중첩된 속성을 직접적으로 최상위로 가져오기 어렵거나, 여러 단계를 거쳐야 합니다. - Code Node 활용:
map()
메서드를 사용하여 각 아이템의 구조를 원하는 대로 변경합니다.
// 입력 데이터 예시:
// [
// { "json": { "id": 1, "name": "Alice", "contact": { "email": "alice@example.com", "phone": "123-4567" } } },
// { "json": { "id": 2, "name": "Bob", "contact": { "email": "bob@example.com", "phone": "987-6543" } } }
// ]
const transformedItems = items.map(item => {
const originalJson = item.json;
return {
json: {
id: originalJson.id,
name: originalJson.name,
email: originalJson.contact.email, // 중첩된 contact.email을 최상위 email로
phone: originalJson.contact.phone // 중첩된 contact.phone을 최상위 phone으로
}
};
});
return transformedItems;
// 출력 데이터 예시 (transformedItems):
// [
// { "json": { "id": 1, "name": "Alice", "email": "alice@example.com", "phone": "123-4567" } },
// { "json": { "id": 2, "name": "Bob", "email": "bob@example.com", "phone": "987-6543" } }
// ]
3. 🚦 복잡한 조건부 로직 구현
여러 조건을 동시에 고려하거나, AND
/OR
조합이 복잡하여 IF
노드 하나로 처리하기 어려울 때 유용합니다.
- 시나리오: 고객의 구매 금액, 멤버십 등급, 최근 구매 이력을 종합하여 VIP 고객에게만 특별 할인 코드를 발급하고 싶습니다.
- 문제점: 여러
IF
노드를 연결하면 워크플로우가 복잡해지고 가독성이 떨어집니다. - Code Node 활용: JavaScript의
if/else if/else
문을 사용하여 모든 조건을 한곳에서 처리합니다.
// 입력 데이터 예시:
// [
// { "json": { "id": "C001", "totalPurchase": 1200, "membership": "Gold", "lastPurchaseDaysAgo": 10 } },
// { "json": { "id": "C002", "totalPurchase": 800, "membership": "Silver", "lastPurchaseDaysAgo": 5 } }
// ]
const resultItems = items.map(item => {
const customer = item.json;
let discountCode = "NONE";
if (customer.totalPurchase >= 1000 && customer.membership === "Gold" && customer.lastPurchaseDaysAgo = 500 && customer.membership === "Silver") {
discountCode = "SILVER_10%"; // 실버 등급, 구매액 500 이상
} else {
discountCode = "WELCOME_5%"; // 그 외 일반 고객
}
// 기존 데이터에 discountCode 추가
return {
json: {
...customer,
discountCode: discountCode
}
};
});
return resultItems;
4. ⏰ 날짜 및 시간 조작
날짜/시간 데이터를 특정 형식으로 변환하거나, 날짜 간의 차이를 계산하는 등 다양한 조작이 필요할 때 활용합니다.
- 시나리오: 데이터베이스에서
timestamp
형식으로 받은 날짜를YYYY-MM-DD HH:MM:SS
형식의 문자열로 변환하여 보고서에 포함하고 싶습니다. - 문제점: 기본 노드로는 날짜 형식 변환이 제한적이거나 복잡할 수 있습니다.
- Code Node 활용: JavaScript의
Date
객체와 메서드를 사용하여 원하는 형식으로 날짜를 포맷합니다. (Moment.js와 같은 라이브러리를 사용하면 더욱 편리하지만, n8n 기본 환경에서는Date
객체를 활용합니다.)
// 입력 데이터 예시:
// [
// { "json": { "eventName": "Login", "timestamp": "2023-10-26T10:30:00.000Z" } },
// { "json": { "eventName": "Logout", "timestamp": "2023-10-26T11:45:00.000Z" } }
// ]
const formattedItems = items.map(item => {
const date = new Date(item.json.timestamp);
// 날짜와 시간 구성
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); // 월은 0부터 시작하므로 +1
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
return {
json: {
...item.json,
formattedTimestamp: formattedDate
}
};
});
return formattedItems;
// 출력 데이터 예시:
// [
// { "json": { "eventName": "Login", "timestamp": "2023-10-26T10:30:00.000Z", "formattedTimestamp": "2023-10-26 10:30:00" } },
// // ...
// ]
5. 🔠 문자열 조작 (정규표현식 포함)
텍스트에서 특정 패턴을 추출하거나, 문자열을 결합, 분리, 대체하는 등 복잡한 문자열 처리가 필요할 때 유용합니다.
- 시나리오: 사용자 입력 텍스트에서 이메일 주소를 추출하거나, 특정 키워드를 다른 단어로 대체하고 싶습니다.
- 문제점: 기본적인 문자열 함수만으로는 한계가 있으며, 정규표현식을 사용해야 할 때가 많습니다.
- Code Node 활용: JavaScript의
String
메서드와 정규표현식(RegExp
)을 사용하여 고급 문자열 조작을 수행합니다.
// 입력 데이터 예시:
// [
// { "json": { "text": "문의사항은 support@example.com으로 보내주세요." } },
// { "json": { "text": "긴급 연락처: admin@company.com 또는 ceo@company.com" } }
// ]
const processedItems = items.map(item => {
const originalText = item.json.text;
// 이메일 주소 추출 (정규표현식 사용)
const emailRegex = /[\w.-]+@[\w.-]+\.\w+/g;
const emails = originalText.match(emailRegex) || []; // 일치하는 모든 이메일 배열로 반환
// 특정 키워드 대체
const replacedText = originalText.replace(/문의사항/g, "궁금한 점");
return {
json: {
originalText: originalText,
extractedEmails: emails,
modifiedText: replacedText
}
};
});
return processedItems;
// 출력 데이터 예시:
// [
// { "json": { "originalText": "문의사항은 support@example.com으로 보내주세요.", "extractedEmails": ["support@example.com"], "modifiedText": "궁금한 점은 support@example.com으로 보내주세요." } },
// // ...
// ]
6. 💫 동적 변수 생성 및 할당
워크플로우 내에서 고유 ID를 생성하거나, 현재 시간, 난수 등 동적인 값을 만들어 데이터에 추가해야 할 때 사용합니다.
- 시나리오: 새로운 고객 등록 시, 고유한 고객 ID와 등록 시각을 자동으로 생성하여 데이터베이스에 저장하고 싶습니다.
- 문제점: 기본 노드로는 복잡한 고유 ID 생성이나 여러 정보의 조합이 어렵습니다.
- Code Node 활용:
Date.now()
,Math.random()
,UUID
생성 로직 등을 사용하여 동적인 값을 생성합니다.
// 입력 데이터 예시:
// [
// { "json": { "name": "Charlie", "email": "charlie@example.com" } }
// ]
const newItems = items.map(item => {
const customer = item.json;
// 간단한 고유 ID 생성 (실제 UUID는 외부 라이브러리 필요)
const uniqueId = `CUST-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
const creationDate = new Date().toISOString(); // ISO 8601 형식
return {
json: {
...customer,
customerId: uniqueId,
registeredAt: creationDate
}
};
});
return newItems;
// 출력 데이터 예시:
// [
// { "json": { "name": "Charlie", "email": "charlie@example.com", "customerId": "CUST-1678888888888-123", "registeredAt": "2023-10-26T14:30:00.000Z" } }
// ]
7. 📊 배열 데이터 집계 (Aggregation)
여러 항목으로 구성된 배열 데이터에서 합계, 평균, 개수 등을 계산하여 요약 정보를 만들 때 매우 유용합니다.
- 시나리오: 특정 주문에 포함된 여러 상품들의 총 가격을 계산하고 싶습니다.
- 문제점:
Item Lists
노드로는 합계, 평균 등의 계산이 제한적입니다. - Code Node 활용:
reduce()
메서드를 사용하여 배열의 값을 집계합니다.
// 입력 데이터 예시:
// [
// { "json": {
// "orderId": "ORD001",
// "products": [
// { "name": "Laptop", "price": 1200, "quantity": 1 },
// { "name": "Mouse", "price": 25, "quantity": 2 }
// ]
// } },
// { "json": {
// "orderId": "ORD002",
// "products": [
// { "name": "Keyboard", "price": 75, "quantity": 1 }
// ]
// } }
// ]
const aggregatedItems = items.map(item => {
const order = item.json;
// products 배열의 각 상품에 대해 (가격 * 수량)을 더하여 총합 계산
const totalAmount = order.products.reduce((sum, product) => {
return sum + (product.price * product.quantity);
}, 0); // 초기 합계는 0
return {
json: {
...order,
totalOrderAmount: totalAmount
}
};
});
return aggregatedItems;
// 출력 데이터 예시:
// [
// { "json": { "orderId": "ORD001", "products": [...], "totalOrderAmount": 1250 } },
// { "json": { "orderId": "ORD002", "products": [...], "totalOrderAmount": 75 } }
// ]
8. 🌐 맞춤형 API 요청/응답 처리
HTTP Request 노드만으로는 처리하기 어려운 복잡한 API 요청 바디 구성이나, 비정형적인 API 응답을 파싱해야 할 때 사용합니다.
- 시나리오: 특정 API가 XML 형식의 요청을 받거나, 응답이 중첩된 문자열로 되어 있어 추가적인 파싱이 필요합니다.
- 문제점: HTTP Request 노드의 설정으로는 한계가 있습니다.
- Code Node 활용:
XML
을 파싱하는 라이브러리(Node.js 환경에 설치된 경우)를 사용하거나,JSON.parse()
를 이용한 다중 파싱 등을 구현합니다. (이 예시에서는 간단한 JSON 문자열 파싱을 보여줍니다.)
// 입력 데이터 예시 (HTTP Request 노드의 응답이라고 가정):
// [
// { "json": { "apiResponseString": "{ \"status\": \"success\", \"data\": { \"user_id\": 123, \"username\": \"johndoe\" } }" } }
// ]
const parsedItems = items.map(item => {
const responseString = item.json.apiResponseString;
let parsedData = {};
try {
// 문자열로 된 JSON 응답을 다시 파싱
const jsonObject = JSON.parse(responseString);
parsedData = jsonObject.data; // "data" 필드만 추출
$node.warn('API 응답 파싱 성공!'); // 디버깅 메시지
} catch (error) {
$node.error(`API 응답 파싱 실패: ${error.message}`); // 에러 로깅
parsedData = { error: "Failed to parse API response" };
}
return {
json: {
...item.json,
parsedApiResponse: parsedData
}
};
});
return parsedItems;
// 출력 데이터 예시:
// [
// { "json": { "apiResponseString": "...", "parsedApiResponse": { "user_id": 123, "username": "johndoe" } } }
// ]
9. ✅ 데이터 유효성 검사
입력 데이터가 특정 조건을 만족하는지 (예: 필수 필드 누락 여부, 데이터 타입 일치 여부, 값 범위) 확인하고, 유효하지 않은 데이터를 걸러낼 때 사용합니다.
- 시나리오: 사용자 등록 폼에서 받은 데이터에
email
과password
필드가 모두 존재하는지, 그리고 이메일 형식이 유효한지 확인하고 싶습니다. - 문제점: 여러
IF
노드와 정규표현식 노드를 조합해야 하므로 복잡해집니다. - Code Node 활용: JavaScript의 조건문과 정규표현식을 사용하여 여러 유효성 검사 규칙을 한 번에 적용합니다.
// 입력 데이터 예시:
// [
// { "json": { "name": "Alice", "email": "alice@example.com", "password": "pass" } },
// { "json": { "name": "Bob", "email": "bob.com" } }, // 이메일 형식 오류, password 누락
// { "json": { "name": "Charlie", "email": "charlie@valid.com" } }
// ]
const validatedItems = items.map(item => {
const data = item.json;
let isValid = true;
let validationErrors = [];
// 1. 필수 필드 확인
if (!data.email) {
isValid = false;
validationErrors.push("Email field is missing.");
}
if (!data.password) {
isValid = false;
validationErrors.push("Password field is missing.");
}
// 2. 이메일 형식 확인 (간단한 정규표현식)
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (data.email && !emailRegex.test(data.email)) {
isValid = false;
validationErrors.push("Invalid email format.");
}
return {
json: {
...data,
isValid: isValid,
validationErrors: validationErrors
}
};
});
return validatedItems;
// 출력 데이터 예시 (부분):
// [
// { "json": { "name": "Alice", "email": "alice@example.com", "password": "pass", "isValid": true, "validationErrors": [] } },
// { "json": { "name": "Bob", "email": "bob.com", "isValid": false, "validationErrors": ["Password field is missing.", "Invalid email format."] } }
// ]
10. 🚨 오류 처리 및 로깅
워크플로우 실행 중 발생할 수 있는 잠재적 오류를 감지하고, 이에 대한 맞춤형 메시지를 생성하거나 특정 에러 로직을 실행할 때 사용합니다.
- 시나리오: 데이터를 처리하는 과정에서 특정 필드가 누락되어 예기치 않은 에러가 발생할 수 있습니다. 에러 발생 시 워크플로우를 중단하지 않고, 에러 정보를 기록하거나 후속 처리(예: 에러 알림 전송)를 하고 싶습니다.
- 문제점: 기본 노드로는
try-catch
와 같은 고급 오류 처리가 어렵습니다. - Code Node 활용: JavaScript의
try...catch
블록을 사용하여 안전하게 코드를 실행하고, 에러 발생 시 사용자 정의 액션을 수행합니다.
// 입력 데이터 예시:
// [
// { "json": { "id": 1, "value": 10 } },
// { "json": { "id": 2, "value": "invalid_number" } }, // 에러 유발 데이터
// { "json": { "id": 3, "value": 20 } }
// ]
const processedItems = items.map(item => {
try {
const originalValue = item.json.value;
const numericValue = parseInt(originalValue);
if (isNaN(numericValue)) {
throw new Error(`Value '${originalValue}' is not a valid number.`);
}
// 정상 처리 로직
const doubledValue = numericValue * 2;
return {
json: {
...item.json,
processedValue: doubledValue,
status: "success"
}
};
} catch (error) {
// 에러 발생 시 처리
$node.warn(`Error processing item ${item.json.id}: ${error.message}`); // n8n UI에 경고 표시
return {
json: {
...item.json,
processedValue: null,
status: "error",
errorMessage: error.message
}
};
}
});
return processedItems;
// 출력 데이터 예시 (부분):
// [
// { "json": { "id": 1, "value": 10, "processedValue": 20, "status": "success" } },
// { "json": { "id": 2, "value": "invalid_number", "processedValue": null, "status": "error", "errorMessage": "Value 'invalid_number' is not a valid number." } },
// // ...
// ]
🚀 Code Node 활용 팁!
Code Node를 효율적으로 사용하기 위한 몇 가지 팁입니다.
- 점진적으로 테스트하세요: 복잡한 코드를 한 번에 작성하기보다, 작은 단위로 작성하고
console.log()
또는$node.warn()
을 사용하여 중간 결과를 확인하며 디버깅하는 것이 좋습니다. items
배열 이해: Code Node는 항상items
라는 배열을 입력으로 받고, 이 배열을 반환해야 합니다. 단일 데이터를 처리할 때는items[0].json
에 접근하고, 여러 데이터를 처리할 때는items.map()
이나items.filter()
를 활용하세요.- 오류 처리:
try...catch
블록을 사용하여 예외 상황을 처리하고,$node.error()
나$node.warn()
으로 n8n UI에 로그를 남기세요. - 가독성 유지: 코드가 복잡해질수록 주석을 달아 코드의 목적과 로직을 명확히 하는 것이 중요합니다.
- 대안 고려: 항상 Code Node가 최선의 선택은 아닙니다. 간단한 데이터 조작이나 조건 분기는 Expression, Set, IF, Merge Node 등 n8n의 기본 노드로도 충분할 수 있습니다. Code Node는 다른 노드로 해결하기 어려운 “마지막 보루”로 생각하세요.
- 성능 고려: 매우 큰 데이터셋을 Code Node로 처리할 경우 성능 문제가 발생할 수 있습니다. 가능한 경우, n8n의 빌트인 노드를 활용하는 것이 더 효율적일 수 있습니다.
🎉 마치며
n8n Code Node는 워크플로우 자동화에 있어 여러분에게 무한한 자유와 유연성을 제공합니다. 기본 노드의 한계를 넘어, 복잡하고 정교한 맞춤형 로직을 구현할 수 있게 해주는 진정한 ‘데이터 마법 지팡이’라고 할 수 있죠. 🧙♀️✨
오늘 살펴본 10가지 실전 예시를 통해 Code Node의 강력함을 느끼셨기를 바랍니다. 처음에는 어렵게 느껴질 수 있지만, 작은 코드부터 시작하여 점진적으로 익숙해지면 여러분의 n8n 워크플로우를 혁신적으로 변화시킬 수 있을 것입니다.
주저하지 말고 직접 Code Node를 추가하고, 여러분의 아이디어를 코드로 구현해보세요! 질문이 있다면 n8n 커뮤니티나 관련 자료를 찾아보면서 해결해나가는 과정 자체가 큰 배움이 될 것입니다.
자동화의 새로운 지평을 여는 여러분의 여정을 응원합니다! 🚀 다음 글에서 또 만나요!