💡 AdPick 캠페인 API를 연결하며 배우는 자바스크립트의 진짜 작동원리
1. 자바스크립트는 ‘한 줄씩 순서대로’ 실행된다
일반적인 코드(=동기, synchronous)는 위에서 아래로 순서대로 실행돼.
예를 들어:
console.log("1"); console.log("2"); console.log("3");
결과는 당연히
이게 바로 동기(synchronous) 방식이야.
모든 작업이 “끝날 때까지 기다렸다가 다음 걸 한다.”
하지만 인터넷 요청처럼 시간이 오래 걸리는 일은 이 방식으로 처리하면 브라우저가 멈춰버려.
2. 그래서 나온 게 “비동기(asynchronous)”
비동기는 “결과가 나올 때까지 기다리지 말고, 다음 일 먼저 해!” 라는 방식이야.
console.log("1"); setTimeout(() => console.log("2"), 1000); console.log("3");
결과는
setTimeout은 1초 기다렸다가 실행되기 때문에, 그 사이에 “3”이 먼저 찍혀.
즉, 기다리지 않는다.
**비동기(asynchronous)**는 CPU를 효율적으로 쓰기 위해 등장한 개념이야.
3. 그런데 비동기는 “나중에” 실행되니까 헷갈린다
그래서 생긴 문법이 Promise야.
“결과를 나중에 주겠다는 약속”이라는 뜻이야. (영어로 promise = 약속)
fetch("https://example.com/data") .then(response => response.json()) .then(data => console.log(data)) .catch(err => console.error(err));
이건 “서버에 요청을 보내고 → 결과가 오면 그 다음에 실행해” 라는 의미야.
이때 fetch()가 Promise를 반환해.
즉 “결과는 나중에 줄게” 라고 약속하는 객체야.
4. async / await — 비동기를 동기처럼 읽히게 해준다
Promise는 .then().then().then() 구조로 이어지니까 길어지고 복잡해져.
그래서 나온 게 async/await 문법.
async function loadData()
{ const res = await fetch("https://example.com/data");
const data = await res.json();
console.log(data);
}
이건 실제로는 비동기지만, 마치 순서대로(동기처럼) 보인다.
await은 “결과가 올 때까지 잠시 멈춰”라는 뜻이고,
async는 “이 함수 안에서는 await를 쓸 거야”라고 표시하는 말이야.
가독성이 좋아지는 이유는?
사람이 “1 → 2 → 3” 순서로 읽듯이 자연스럽게 읽히기 때문이야.
5. HTTP란 무엇인가
HTTP는 HyperText Transfer Protocol,
즉 “인터넷에서 정보를 주고받는 규칙(프로토콜)”이야.
너가 브라우저 주소창에 뭘 입력하든,
실제로는 브라우저가 서버에 “요청(request)”을 보내고,
서버는 “응답(response)”을 돌려주는 방식이야.
fetch()는 바로 이 HTTP 요청을 보내는 브라우저 내장 함수야.
6. CORS와 SOP — 보안을 위한 제약
SOP (Same-Origin Policy)
→ 브라우저는 같은 출처(=도메인)에서 온 코드만 신뢰한다.
다른 사이트로 요청하려면 서버가 허락해야 해.
CORS (Cross-Origin Resource Sharing)
→ 다른 출처의 요청을 “허용”하는 방법.
서버가 “Access-Control-Allow-Origin” 헤더를 보내야 한다.
예를 들어,
네 블로그가 https://myblog.com이고,
https://adpick.co.kr에 요청을 보내면 출처가 다르기 때문에 CORS가 필요하다.
7. 프록시(Proxy) — 대신 요청해주는 서버
CORS를 해결하는 가장 쉬운 방법은 프록시를 두는 거야.
브라우저 → 프록시(내 서버) → AdPick 서버 → 프록시 → 브라우저
이렇게 중간에 “대신 요청하는 서버”를 하나 세우는 거지.
Cloudflare Worker가 바로 그 역할을 해.
내 도메인이니까 브라우저는 CORS 문제 없이 접근할 수 있어.
8. Worker 코드 뜯어보기
export default {
async fetch(request, env, ctx) {
const target = "https://adpick.co.kr/apis/offers.php";
const url = new URL(request.url);
const affid = url.searchParams.get("affid") || "5ac7a6";
const categories = ["", "game", "notgame"];
const randomCat = categories[Math.floor(Math.random() * categories.length)];
const apiUrl = `${target}?affid=${affid}&category=${randomCat}&order=rand&_=${Date.now()}`;
const result = await fetch(apiUrl, { headers: { "User-Agent": "Mozilla/5.0" } });
const data = await result.text();
return new Response(data, {
headers: {
"Content-Type": "application/json; charset=utf-8",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET,HEAD,OPTIONS",
"Cache-Control": "no-store",
},
});
},
};
- export default
→ “이 파일의 기본 내보내기(export)”라는 의미. Cloudflare는 이걸 찾아 실행함. - async fetch(request, env, ctx)
→ 이 함수는 HTTP 요청이 올 때마다 실행된다.- request: 요청 정보 (URL, 헤더, 바디 등)
- env: 환경변수 (DB나 비밀키 저장할 때)
- ctx: 비동기 작업을 백그라운드로 넘길 때 사용
- new URL(request.url)
→ 문자열을 URL 객체로 만들어서 .searchParams 같은 편리한 기능 사용 가능.
new는 “객체를 새로 생성한다”는 뜻. - url.searchParams.get("affid")
→ URL의 ?affid=5ac7a6 값을 읽어온다. 없으면 "5ac7a6" 사용. - categories[Math.floor(Math.random() * categories.length)]
→ 배열에서 랜덤으로 하나를 고름.- Math.random() → 0~1 사이의 랜덤 숫자
- Math.floor() → 소수점 아래 버림
- ${Date.now()}
→ 지금 시각을 밀리초 단위로 반환 (캐시 무력화) - return new Response(...)
→ Worker가 응답을 만들어서 브라우저로 돌려줌.
9. fetch 응답 다루기
res.text() → 응답을 문자열로 받는다
res.json() → 응답을 JSON 객체로 받는다
JSON.parse(text) → 문자열을 JSON 객체로 바꾼다 (파싱, parsing)
**파싱(parsing)**은 “텍스트를 구조화된 데이터로 해석하는 것”.
예를 들어,
'{"name": "Jin", "age": 30}'
이런 문자열을 JS 객체로 바꿔야 실제로 데이터를 쓸 수 있다.
10. then / catch / => 이게 다 뭔가?
fetch(url)
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
- .then()
→ “결과가 오면 이걸 실행해” - .catch()
→ “에러가 나면 이걸 실행해” - =>
→ 화살표 함수(arrow function).
res => res.json()은 function(res) { return res.json(); }의 짧은 버전이야. - res
→ response(응답)의 약자. fetch가 반환한 결과를 가리킴.
11. 이벤트 루프(Event Loop)란?
자바스크립트는 싱글 스레드(single thread), 즉 한 번에 한 일만 한다.
그럼 비동기 코드를 어떻게 동시에 처리할까?
비동기 작업들은 “대기 큐(queue)”에 넣어두고,
메인 코드가 끝나면 이벤트 루프가 하나씩 꺼내 실행해.
그래서 setTimeout, fetch 같은 작업이 끝난 후 나중에 실행되는 거야.
이 구조 덕분에 자바스크립트는 멈추지 않고 계속 반응할 수 있어.
12. return은 왜 쓰냐?
return은 “이 함수가 이 값을 돌려준다”는 뜻이야.
Worker의 return new Response()는 “이게 최종 응답이야!” 라는 표시.
함수가 끝날 때 항상 뭔가 돌려줘야 브라우저가 받을 수 있지.
🧩 한자·영단어 정리
- 비동기(非同期) / asynchronous
非(아닐 비) 同(같을 동) 期(기약할 기)
asyn- (not together) + chronos(시간) - 약속(約束) / promise
約(맺을 약) 束(묶을 속)
pro-(앞으로) + mittere(보내다) - 요청(要請) / request
要(요할 요) 請(청할 청)
re-(다시) + quaerere(찾다) - 응답(應答) / response
應(응할 응) 答(대답할 답)
re-(back) + spondere(맹세하다) - 교차출처(交叉出處) / CORS (Cross-Origin Resource Sharing)
交(사귈 교) 叉(갈래 차) 出(날 출) 處(곳 처) - 프록시(代理) / proxy
代(대신할 대) 理(다스릴 리)
라틴어 procuratio (대리 행위) - 파싱(解析) / parsing
解(풀 해) 析(쪼갤 석)
라틴 pars (부분, 조각) - 헤더(頭部) / header
頭(머리 두) 部(떼 부)
영어 head(머리) + -er(명사형 접미사) - 함수(函數) / function
函(상자 함) 數(셀 수)
라틴 functio(작동, 수행) - 매개변수(媒介變數) / parameter
媒(중매할 매) 介(낄 개) 變(변할 변) 數(셀 수)
para-(곁에) + metron(측정)
💬 요약
- fetch()는 브라우저 내장 HTTP 요청 도구다.
- Promise는 “결과를 나중에 받는 약속”이다.
- async/await은 비동기를 동기처럼 읽게 만든다.
- SOP/CORS는 보안을 위한 같은 출처 정책이다.
- 프록시는 브라우저 대신 요청해주는 중간 서버다.
- 파싱은 문자열을 구조화된 데이터로 해석하는 과정이다.
- return, =>, .then, .catch, event loop 등은
전부 비동기 흐름을 제어하기 위한 자바스크립트의 핵심 기초 문법이다.
'Web > TIP' 카테고리의 다른 글
🔗 API, 서버, 프록시, JSON, CORS — 웹 통신의 다섯 기둥 (0) | 2025.10.10 |
---|