ES5 자바스크립트와 Cobweb 스크래핑 개발 실무 가이드
- 개요
- 연간 교육 계획과 Cobweb 개발 로드맵
- Cobweb 아키텍처와 JS 모듈 구조 이해
- Cobweb 모듈의 실행 흐름
- ES5 자바스크립트 핵심 기능의 실무 적용
- 일반적인 스크래핑 패턴 구현
- 자바스크립트 강의 내용과 Cobweb 실무 적용
- 주요 실무 패턴 분석
- 자주 사용되는 ES5 스크래핑 기법
- 실무 적용을 위한 개발 팁
1. 개요
이 문서는 "웹 프론트엔드를 위한 자바스크립트 첫걸음" 강의와 Cobweb 개발 환경을 연계하여, 실무에서 ES5 자바스크립트를 효과적으로 활용하는 방법을 안내합니다.
Cobweb은 Object-C/Cocoa 기반의 스크래핑 도구로, espiderKit 프레임워크를 통해 자바스크립트 모듈을 실행하는 구조입니다.
이러한 환경에서 ES5 자바스크립트 지식을 어떻게 실제 스크래핑 개발에 적용할 수 있는지 알아보겠습니다.
2. 연간 교육 계획과 Cobweb 개발 로드맵
2.1 분기별 교육 및 실무 적용 계획
분기 |
교육 주제 |
Cobweb 실무 적용 |
예상 성과 |
1분기 |
웹 프론트엔드를 위한 자바스크립트 첫걸음 |
• 기초 자바스크립트 문법 및 DOM 활용 방법 • ES5 기반 코드 작성 및 코브웹 개발툴 활용법 |
스크래핑 모듈 개발의 기본 역량 확보 |
2분기 |
보안 인증 우회 및 고급 로그인 처리 기법 |
• 공동인증서 기반 로그인 시스템 분석 및 대응 • 금융권/공공기관 웹사이트 인증 절차 자동화 • 2FA(이중 인증) 처리 방법 • 세션 관리 및 쿠키 기반 인증 유지 |
보안 사이트 스크래핑 역량 강화 |
3분기 |
데이터 암호화/복호화 및 보안 통신 처리 |
• HTTPS 통신 패턴 분석 및 처리 • 웹사이트 데이터 암호화 방식 이해 (AES, RSA 등) • 암호화된 페이로드 분석 및 복호화 기법 • 웹 요청 서명(Signature) 분석 및 재현 • API 요청 암호화 패턴 모방 • 난독화된 JavaScript 코드 분석 |
복잡한 네트워크 통신 처리 능력 향상 |
4분기(미정) |
분산 아키텍처 기반 대규모 스크래핑 시스템 구축 |
• IP 차단 대응을 위한 프록시 풀 구현 • 대용량 데이터 처리 및 저장 최적화 • 컨테이너화 및 마이크로서비스 기반 확장성 확보 |
장애 복구 및 자동화된 운영 체계 구현 |
4분기(미정) |
고급 동적 콘텐츠 스크래핑 및 성능 최적화 |
• SPA(Single Page Application) 및 동적 웹사이트 스크래핑 기법 • JavaScript 렌더링 엔진 기반 스크래핑 구현 • 웹소켓 통신 감시 및 데이터 추출 • 인피니트 스크롤 및 동적 로딩 처리 전략 • 스크래핑 성능 최적화 및 병목 현상 해결 • 메모리 누수 방지 및 장시간 실행 안정성 확보 |
리소스 효율적인 스크래핑 시스템 구현 |
3. Cobweb 아키텍처와 JS 모듈 구조 이해
3.1 Cobweb의 JavaScript 실행 구조
Cobweb은 Object-C/Cocoa 기반의 스크래핑 도구로, espiderKit 프레임워크를 통해 자바스크립트 모듈을 실행하는 구조입니다.
다음과 같은 구조로 JavaScript 모듈을 실행합니다.
더보기
┌─────────────────────────────────────────┐
│ Cobweb Application │
│ ┌─────────────────┐ ┌───────────────┐ │
│ │ Cocoa UI Layer │ │ Configuration │ │
│ └─────────────────┘ └───────────────┘ │
│ ┌─────────────────────────────────────┐│
│ │ espiderKit Framework ││
│ │ ┌─────────────┐ ┌─────────────┐ ││
│ │ │ Spider │◄──┤ JavaScript │ ││
│ │ │ Engine │ │ Interpreter │ ││
│ │ └─────────────┘ └─────────────┘ ││
│ └─────────────────────────────────────┘│
└─────────────────────────────────────────┘
espiderKit 프레임워크는 내부적으로 JavaScript 실행 환경을 포함하고 있으며, 이를 통해 ES5 자바스크립트로 작성된 스크래핑 모듈을 실행합니다. Object-C 코드와 JavaScript 코드는 이 실행 환경을 통해 상호 작용합니다.
3.2 모듈 상속 구조
Cobweb에서 JavaScript 모듈은 다음과 같은 상속 구조를 가집니다.
Module.prototype.js (기본 모듈)
↑
KROM0016.js (사이트 카테고리별 모듈)
↑
KROM0016011000.js (무신사 주문 조회 모듈)
이 구조에서 각 레벨은 다음과 같은 역할을 담당합니다.
- 기본 모듈(Module.prototype.js): 모든 스크래핑 모듈의 기본 기능 제공
- 카테고리 모듈(KROM0016.js): 특정 카테고리(예: 쇼핑몰 주문조회)에 공통적인 기능 제공
- 사이트별 모듈(KROM0016011000.js): 특정 사이트(예: 무신사)에 맞춘 구체적인 스크래핑 로직 구현
4. Cobweb 모듈의 실행 흐름
4.1 모듈 실행 라이프사이클
Cobweb에서 JavaScript 모듈은 다음과 같은 라이프사이클로 실행됩니다.
Init() → GetParam() → Login() → GetData() → Convert() → SetResult()
각 단계는 다음과 같은 역할을 담당합니다.
- Init(): 모듈 초기화, HTTP 객체 생성 및 기본 설정
- GetParam(): 실행 파라미터 획득 및 검증
- Login(): 대상 사이트 로그인 처리
- GetData(): 실제 데이터 스크래핑 수행
- Convert(): 스크래핑한 데이터를 표준 형식으로 변환
- SetResult(): 최종 결과 반환
각 단계는 boolean 값을 반환하며, 하나라도 false를 반환하면 모듈 실행이 중단됩니다.
4.2 핵심 코드 패턴 분석
무신사 스크래핑 모듈 코드에서 볼 수 있는 핵심 패턴을 분석해보겠습니다.
모듈 정의와 상속
var KROM0016011000 = function () {
// 모듈 속성 및 메서드 정의
this.retData = {}; // 결과 데이터
this.tempData = {}; // 임시 데이터
// 메서드 구현
this.Init = function() { /* ... */ }
// ...
}
// 부모 모듈 상속
KROM0016011000.prototype = new KROM0016();
HTTP 통신 처리
this.http = new HttpProtocol();
this.http.timeout = 30;
this.http.urlencode = true;
this.http.isSSLHostname = true;
// HTTP 요청 예시
this.http.setRequestHeader("Connection", "Keep-Alive");
resultData = this.httpConnect("", "GET", this.hostURL + getData, "", 4);
JSON 데이터 처리
try {
resultJson = JSON.parse(resultData);
} catch (e) {
this.userError = (new Error().lineNumber + 1) + "";
this.error = ERROR_ESPIDER.PAGE_CHANGE;
this.tracking(resultData, this.debugDate, true);
return false;
}
// 안전한 데이터 접근
this.safeGet = function(d) {
d = (d === null || d === undefined) ? "" : d;
if (typeof d == "number") d = d + "";
return d;
};
오류 처리
this.userError = (new Error().lineNumber + 1) + "";
this.error = ERROR_ESPIDER.PAGE_CHANGE;
this.tracking(resultData, this.debugDate, true);
return false;
5. ES5 자바스크립트 핵심 기능의 실무 적용
5.1 프로토타입 기반 상속
Cobweb 모듈은 ES5의 프로토타입 상속을 활용하여 코드 재사용성을 높입니다.
// 부모 클래스 정의
var BaseModule = function() {
this.commonMethod = function() {
// 공통 기능 구현
}
}
// 자식 클래스 정의
var ChildModule = function() {
this.specificMethod = function() {
// 특화 기능 구현
}
// 부모 메서드 오버라이드
this.commonMethod = function() {
// 기본 구현 확장
BaseModule.prototype.commonMethod.call(this);
// 추가 기능 구현
}
}
// 상속 설정
ChildModule.prototype = new BaseModule();
5.2 클로저와 스코프
ES5의 클로저를 활용하여 모듈 내부 상태를 캡슐화할 수 있습니다.
var ScrapeModule = function() {
// 프라이빗 변수
var privateData = {};
var requestCount = 0;
// 프라이빗 함수
function processData(rawData) {
// 내부 처리 로직
requestCount++;
return /* 처리된 데이터 */;
}
// 퍼블릭 메서드 (클로저를 통해 프라이빗 변수/함수 접근)
this.getData = function() {
var rawData = this.httpConnect(/* ... */);
return processData(rawData);
}
this.getRequestCount = function() {
return requestCount;
}
}
6. 일반적인 스크래핑 패턴 구현
6.1 선택자 시스템 구현
웹사이트 구조 변경에 대응하기 위한 견고한 선택자 시스템 예시
function findElement(container, selectors) {
if (typeof selectors === 'string') {
selectors = [selectors];
}
for (var i = 0; i < selectors.length; i++) {
var element = container.querySelector(selectors[i]);
if (element) return element;
}
return null;
}
// 사용 예시
var priceElement = findElement(productCard, [
'.product-price',
'.price',
'[data-price]',
'strong.value'
]);
6.2 데이터 추출 및 정제
다양한 형식의 데이터를 일관되게 추출하고 정제하는 유틸리티
var DataExtractor = {
// 텍스트 추출
getText: function(element, selectors, defaultValue) {
defaultValue = defaultValue || '';
var targetEl = findElement(element, selectors);
return targetEl ? targetEl.textContent.trim() : defaultValue;
},
// 가격 추출 및 정규화
extractPrice: function(element, selectors) {
var priceText = this.getText(element, selectors);
return priceText.replace(/[^\d]/g, '');
},
// 날짜 추출 및 변환
extractDate: function(element, selectors, format) {
var dateText = this.getText(element, selectors);
// 날짜 형식 변환 로직 구현
return formattedDate;
}
};
6.3 동적 콘텐츠 처리
동적으로 로드되는 콘텐츠 처리 전략
function waitForElement(selector, timeout) {
timeout = timeout || 10000; // 기본 10초 타임아웃
var startTime = new Date().getTime();
function checkElement() {
var element = document.querySelector(selector);
if (element) {
return element;
}
if (new Date().getTime() - startTime > timeout) {
throw new Error("Element not found within timeout: " + selector);
}
// 잠시 대기 후 다시 확인
return null;
}
// 요소가 나타날 때까지 반복 확인
while (true) {
var element = checkElement();
if (element) return element;
// JavaScript에서는 동기적 대기가 불가능하므로 실제 구현은 다를 수 있음
// Cobweb 환경에서는 별도의 대기 메커니즘 사용 필요
}
}
6.4 데이터 검증 및 정합성 확인
데이터 검증 및 정합성 확인 패턴
function validateData (data, schema) {
for (var key in schema) {
if (schema.hasOwnProperty(key)) {
var validator = schema[key];
var value = data[key];
if (!validator(value)) {
return {
valid: false,
field: key,
value: value
};
}
}
}
return { valid: true };
}
// 사용 예시
var orderSchema = {
orderNo: function(v) { return v && v.length > 0; },
orderDate: function(v) { return /^\d{8}$/.test(v); },
amount: function(v) { return !isNaN(v) && v >= 0; }
};
var validationResult = validateData(orderData, orderSchema);
if (!validationResult.valid) {
// 검증 오류 처리
}
7. 자바스크립트 강의 내용과 Cobweb 실무 적용
7.1 강의 주제별 Cobweb 실무 연계
강의 주제 |
Cobweb 모듈 적용 포인트 |
실제 코드 예시 |
변수와 상수 |
데이터 저장 구조 활용 |
this.retData = {}; this.tempData = {}; |
자료형과 형변환 |
문자열 <-> 숫자 변환 |
Number(this.tempData.searchStDt) |
조건문 |
에러 처리 및 분기 로직 |
if (!resString) { this.error = ERROR_ESPIDER.LOGIN_PARAMTER; return false; } |
함수 |
모듈화된 스크래핑 로직 |
this.getOrderData = function(orderItem, orderNo) { ... } |
스코프, 호이스팅 |
변수 범위 관리 |
모듈 내부 변수와 함수 스코프 |
객체 |
데이터 구조화 |
var orderJson = { orderNo: orderNumber, orderDate: orderDate[0] }; |
배열과 반복문 |
목록 데이터 처리 |
for (var item of orderList) { ... } |
DOM API |
웹페이지 데이터 추출 |
실제 스크래핑 로직에서 DOM 요소 선택 및 데이터 추출 |
JSON 처리 |
서버 응답 파싱 |
resultJson = JSON.parse(resultData); |
비동기 처리 |
HTTP 요청 관리 |
resultData = this.httpConnect(...); |
7.2 ES5 문법의 실무 활용
// 1. 변수와 스코프 (강의 섹션 2, 7)
var searchStDt = this.tempData.searchStDt;
var searchEndDt = this.tempData.searchEndDt;
// 2. 조건문 (강의 섹션 5)
if (searchStDt == "" && searchEndDt == "") {
// 조회일자 미입력 시 자동으로 현재일 기준 최근 5년 이내 내역을 조회
this.tempData.searchStDt = resultJson.startDate.replace(/\-/g, '');
this.tempData.searchEndDt = resultJson.endDate.replace(/\-/g, '');
}
// 3. 객체 활용 (강의 섹션 10)
var orderJson = {
orderNo: orderNumber, // 주문번호
orderDate: orderDate[0].replace(/\./g, ''), // 날짜(일자)
detailList: [] // 주문내역 목록
};
// 4. 배열과 반복문 (강의 섹션 11, 12)
for (var item of orderOptionList) {
var detailJson = {};
detailJson.productName = item.goodsName;
detailJson.quantity = item.quantity + "";
// ... 추가 필드
orderJson.detailList.push(detailJson);
}
// 5. 정규표현식 활용 (자료형 처리)
orderJson.orderDate = orderDate[0].replace(/\./g, '');
8. 주요 실무 패턴 분석
8.1 데이터 추출 패턴
Cobweb에서 웹사이트 데이터를 추출하는 핵심 패턴입니다.
// HTTP 요청을 통한 데이터 획득
resultData = this.httpConnect(new Error().lineNumber + 1, "GET",
"/order-service/my/order/get_order_view/" + orderNumber, "", 0);
if (resultData === false) return false;
try {
// JSON 파싱 (강의 섹션 비동기처리/API 호출)
resultJson = JSON.parse(resultData);
} catch (e) {
this.userError = (new Error().lineNumber + 1) + "";
this.error = ERROR_ESPIDER.PAGE_CHANGE;
this.tracking(resultData, this.debugDate, true);
return false;
}
// 데이터 검증 및 안전한 접근 (강의 섹션 조건문/객체)
var orderList = this.safeGet(resultJson.orderList);
if (!orderList || orderList == "") {
// 에러 처리
return false;
}
8.2 데이터 정제 및 변환
스크래핑한 원시 데이터를 표준 형식으로 가공하는 패턴입니다.
// Convert 메소드의 핵심 패턴
for (var i = 0; i < orderList.length; i++) {
var rec = orderList[i];
var orderJson = {};
orderJson[MODULE_ESPIDER.RES_OM_DATE_KEY] = "" + rec.orderDate;
orderJson[MODULE_ESPIDER.RES_OM_ORDER_NUMBER_KEY] = "" + rec.orderNo;
// ... 추가 필드 매핑
orderJson[MODULE_ESPIDER.RES_OM_ORDER_DETAIL_LIST_KEY] = [];
for (var j = 0; j < rec.detailList.length; j++) {
var dt_rec = rec.detailList[j];
var orderDetailJson = {};
orderDetailJson[MODULE_ESPIDER.RES_OM_PRODUCT_NAME_KEY] = "" + dt_rec.productName;
// ... 추가 필드 매핑
orderJson[MODULE_ESPIDER.RES_OM_ORDER_DETAIL_LIST_KEY].push(orderDetailJson);
}
this.retData[MODULE_ESPIDER.RES_RESULT_KEY][MODULE_ESPIDER.RES_LISTS_KEY].push(orderJson);
}
8.3 안전한 데이터 접근
Cobweb에서 널 체크와 타입 변환을 통한 안전한 데이터 접근 패턴입니다.
// 안전한 데이터 획득 헬퍼 함수
this.safeGet = function(d) {
d = (d === null || d === undefined) ? "" : d;
if (typeof d == "number") d = d + ""; //숫자인 경우 문자열로 변경
return d;
};
// 사용 예
var orderList = this.safeGet(resultJson.orderList);
if (!orderList || orderList == "") {
// 에러 처리
}
9. 자주 사용되는 ES5 스크래핑 기법
9.1 데이터 포맷 변환
날짜, 금액 등의 데이터 포맷을 변환하는 기법입니다.
// 날짜 포맷 변환 (YYYY-MM-DD → YYYYMMDD)
searchStDt = this.tempData.searchStDt ?
this.tempData.searchStDt.substr(0, 4) + "-" +
this.tempData.searchStDt.substr(4, 2) + "-" +
this.tempData.searchStDt.substr(6, 2) : "";
// 금액에서 콤마 제거
detailJson.amount = item.receiveAmount.replace(/\,/g, '');
9.2 데이터 검증 및 오류 처리
데이터 유효성 검증과 오류 처리 기법입니다.
// 필수 데이터 검증
if (
detailJson.productName == "" ||
detailJson.quantity == "" ||
detailJson.amount == "" ||
detailJson.option == "" ||
detailJson.state == "" ||
detailJson.lnkUrl == "" ||
detailJson.productCode == ""
) {
this.userError = (new Error().lineNumber + 1) + "";
this.error = ERROR_ESPIDER.PAGE_CHANGE;
this.tracking(JSON.stringify(item) + "\n\n[resultData]\n" + resultData, this.debugDate, true);
return false;
}
// 날짜 검증
if (!searchStDt.checkDateString()) {
this.error = ERROR_ESPIDER.LOGIC_CHECK_DATE;
this.userError = (new Error().lineNumber + 1) + "";
return false;
}
10. 실무 적용을 위한 개발 팁
10.1 ES5 호환성 유지하기
최신 자바스크립트 기능을 사용하지 않고 ES5 호환성을 유지하는 방법:
// ❌ 사용하지 않음: let, const (ES6)
let orderList = resultJson.list;
const hostURL = "https://www.musinsa.com";
// ✅ 사용함: var (ES5)
var orderList = resultJson.list;
this.hostURL = "https://www.musinsa.com";
// ❌ 사용하지 않음: 화살표 함수 (ES6)
orderList.forEach(item => {
// 처리
});
// ✅ 사용함: 함수 표현식 (ES5)
for (var i = 0; i < orderList.length; i++) {
var item = orderList[i];
// 처리
}
// ❌ 사용하지 않음: Promise, async/await (ES6+)
async function fetchData() {
const response = await fetch(url);
}
// ✅ 사용함: 콜백 기반 비동기 처리 (ES5)
this.httpConnect(lineNum, "GET", url, "", 0, function(result) {
// 응답 처리
});
10.2 디버깅 및 로깅 최적화
스크래핑 모듈의 디버깅과 로깅을 효과적으로 하는 방법:
// 라인 번호 추적으로 오류 위치 파악
this.userError = (new Error().lineNumber + 1) + "";
// 상세 컨텍스트 정보 포함 로깅
this.tracking(
JSON.stringify(item) + "\n\n[resultData]\n" + resultData,
this.debugDate, // ex) "20251231"
true
);
// 중요 단계별 로깅
console.info("무신사 모듈 KROM0016011000 class 4.GetData :: module index[",
this.index, "] thread index[", this.threadIndex, "]");