λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°

Web

[Web] CORS

ν•œ μ‚¬μ΄νŠΈμ—μ„œ μ£Όμ†Œκ°€ λ‹€λ₯Έ μ„œλ²„λ‘œ μš”μ²­μ„ λ³΄λ‚Όλ•Œ 이 CORS에 μ˜ν•œ μ—λŸ¬κ°€ λ°œμƒν•˜κ²Œ λœλ‹€. ν•˜μ§€λ§Œ μ‹ κΈ°ν•˜κ²Œλ„ 같은 μš”μ²­μ΄λ”λΌλ„ μ–΄λ–€ λΆ€λΆ„μ—μ„œλŠ” 되고 또 λ‹€λ₯Έ λΆ€λΆ„μ—μ„œλŠ” μ•ˆλ˜λŠ” κ²½μš°κ°€ μžˆλŠ”λ° μ΄λ•Œ CORS둜 μš”μ²­μ„ λ§‰λŠ” 것은 λΈŒλΌμš°μ €μ˜ μ˜μ—­μ΄λ‹€.

μ™œλƒλ©΄ μš°λ¦¬μ˜ λΈŒλΌμš°μ €λŠ” μš°λ¦¬λ“€μ΄ λ°©λ¬Έν•˜λŠ” μ‚¬μ΄νŠΈλ₯Ό λ―Ώμ§€ λͺ»ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

πŸ“˜ CORS(Cross-Origin Resource Sharing)

MDN - CORS

ꡐ차 좜처 λ¦¬μ†ŒμŠ€ 곡유(Cross-Origin Resource Sharing, CORS)λŠ” μ„œλ²„κ°€ λΈŒλΌμš°μ €κ°€ λ¦¬μ†ŒμŠ€ λ‘œλ“œλ₯Ό ν—ˆμš©ν•΄μ•Ό ν•˜λŠ” μžμ²΄κ°€ μ•„λ‹Œ λ‹€λ₯Έ 좜처 (도메인, 체계 λ˜λŠ” 포트) λ₯Ό λ‚˜νƒ€λ‚Ό 수 μžˆλ„λ‘ ν•˜λŠ” HTTP 헀더 기반 λ©”μ»€λ‹ˆμ¦˜μž…λ‹ˆλ‹€ . CORSλŠ” λ˜ν•œ μ„œλ²„κ°€ μ‹€μ œ μš”μ²­μ„ ν—ˆμš©ν•˜λŠ”μ§€ ν™•μΈν•˜κΈ° μœ„ν•΄ λΈŒλΌμš°μ €κ°€ ꡐ차 좜처 λ¦¬μ†ŒμŠ€λ₯Ό ν˜ΈμŠ€νŒ…ν•˜λŠ” μ„œλ²„μ— “ν”„λ¦¬ν”ŒλΌμ΄νŠΈ” μš”μ²­μ„ ν•˜λŠ” λ©”μ»€λ‹ˆμ¦˜μ— μ˜μ‘΄ν•©λ‹ˆλ‹€. ν•΄λ‹Ή ν”„λ¦¬ν”ŒλΌμ΄νŠΈμ—μ„œ λΈŒλΌμš°μ €λŠ” HTTP λ©”μ„œλ“œλ₯Ό λ‚˜νƒ€λ‚΄λŠ” 헀더와 μ‹€μ œ μš”μ²­μ— μ‚¬μš©λ  헀더λ₯Ό λ³΄λƒ…λ‹ˆλ‹€.

 

MDNμ—μ„œλŠ” μœ„μ™€ 같이 CORSλ₯Ό μ„€λͺ…ν•œλ‹€. 사싀 μ΄λ ‡κ²Œ λ“€μ–΄μ„œλŠ” 아직 크게 λ¬΄μŠ¨λ§μΈμ§€ 와닿지 μ•Šμ•˜λ‹€. κ·Έλž˜μ„œ λ¨Όμ € CORSκ°€ μ™œ λ“±μž₯ν–ˆλŠ”μ§€λΆ€ν„° μ°Ύμ•„λ³΄μ•˜λ‹€.

 

πŸ“’ CORS의 λ“±μž₯ λ°°κ²½

λ‚˜μ˜ λΈŒλΌμš°μ €μ—λŠ” ν† ν°λ“±μ˜ 정보듀이 μΏ ν‚€μ˜ ν˜•νƒœλ‘œ μ €μž₯이 λ˜μ–΄μžˆλ‹€. λ§Œμ•½ λ‚΄κ°€ μ•…μ˜μ μΈ μ‚¬μ΄νŠΈμ— μ ‘μ†ν–ˆμ„λ•Œ μ•…μ˜μ μΈ μ‚¬μ΄νŠΈμ—μ„œλŠ” λ‚˜λ₯Ό μœ„ν•΄μ„œ μ•…μ˜μ μΈ μ‚¬μ΄νŠΈμ˜ html, css, js 파일 등을 보내주고 λ‚΄ λΈŒλΌμš°μ €λŠ” λ‚˜μ—κ²Œ 보여주기 μœ„ν•΄μ„œ μ½”λ“œλ“€μ„ νŒŒμ‹±ν•˜κ³  ν† ν°ν™”ν•˜λŠ” λ“± 받은 μ½”λ“œλ“€μ΄ λ™μž‘λ˜λ„λ‘ 할텐데 μ΄λ•Œ λ§Œμ•½ μžλ°”μŠ€ν¬λ¦½νŠΈλ‘œ λΈŒλΌμš°μ €μ— μ €μž₯된 λ‚΄ 정보λ₯Ό λΉΌλ‚΄μ„œ ν•΄λ‹Ή μ‚¬μ΄νŠΈλ“€λ‘œ μ ‘κ·Όν•΄μ„œ λ‚΄ κ°œμΈμ •λ³΄λ₯Ό λΉΌμ•˜μ„ 수 μžˆμ§€ μ•Šμ„κΉŒ?

 

λ‹€ν–‰νžˆλ„ 우리의 λΈŒλΌμš°μ €λŠ” μ–΄λ–€ μ‚¬μ΄νŠΈμ—μ„œ λ‹€λ₯Έ μ‚¬μ΄νŠΈλ‘œ μš”μ²­μ„ λ³΄λ‚΄λŠ” 것을 기본적으둜 막아쀀닀. κ·Έλ ‡μ§€λ§Œ μ›Ή μƒνƒœκ³„κ°€ 점점 더 λ‹€μ–‘ν•΄μ§€λ©΄μ„œ μ—¬λŸ¬ μ„œλΉ„μŠ€λ“€κ°„μ— 보닀 자유둭게 데이터λ₯Ό μ£Όκ³  받을 ν•„μš”κ°€ μƒκΈ°κ²Œ λ˜μ—ˆλŠ”λ° λ‹€λ₯Έ μ‚¬μ΄νŠΈλ‘œ μš”μ²­μ„ 보내야 ν•˜κΈ° λ•Œλ¬Έμ— 이λ₯Ό ν—ˆμš©ν•˜λ„둝 ν•  수 μžˆλŠ” CORSκ°€ ν•„μš”ν•΄μ§€κ²Œ λ˜μ—ˆλ‹€.

 

πŸ“™ SOP (Same Origin Policy)

CORSλŠ” μ–΄λ–€ μ‚¬μ΄νŠΈμ—μ„œ λ‹€λ₯Έ μ‚¬μ΄νŠΈλ‘œ μš”μ²­μ„ λ³΄λ‚΄λŠ” 것을 ν—ˆμš©ν•˜λŠ” 것이닀. 반면 SOP은 동일 좜처 μ •μ±…μœΌλ‘œμ„œ 같은 URL끼리만 APIλ“±μ˜ 데이터 접근이 κ°€λŠ₯ν•˜λ„λ‘ ν•˜λŠ” 정책이닀.

λ‚΄κ°€ λ‹€λ₯Έ 좜처둜 μš”μ²­(Cross-Origin μš”μ²­)을 ν•˜κ²Œ λ˜λŠ” 경우 λΈŒλΌμš°μ €μ—μ„œλŠ” μš”μ²­μ— origin이라고 Header에 μΆ”κ°€ν•΄μ„œ μš”μ²­μ„ ν•œλ‹€.

GET /HTTP/1.1
Host: www.jongbin.com
Origin: https://www.jongbin.com

μ΄λŸ¬ν•œ μš”μ²­μ„ 받은 μ„œλ²„λŠ” λ‹΅μž₯ 헀더에 μ§€μ •λ˜μ–΄ μžˆλŠ” Access-Control-Allow-Origin 정보λ₯Ό 보낸닀.

Access-Control-Allow-Origin: www.jongbin.com
Access-Control-Allow-Origin: *

이후 λΈŒλΌμš°μ €λŠ” 이 λ‘˜μ„ λΉ„κ΅ν•œλ‹€. originμ—μ„œ 보낸 μΆœμ²˜κ°’μ΄ μ„œλ²„μ˜ λ‹΅μž₯ 헀더에 μžˆλŠ” Access-Control-Allow-Origin에 μ‘΄μž¬ν•œλ‹€λ©΄ μ•ˆμ „ν•œ μš”μ²­μ΄λΌκ³  νŒλ‹¨ν•˜κ³  데이터λ₯Ό κ±΄λ„€μ£Όκ²Œ λœλ‹€.

μ‚¬μš©μž 식별 정보가 λ‹΄κΈ΄ μš”μ²­μ„ ν•˜λŠ” κ²½μš°μ—λŠ” 더 μ—„κ²©ν•˜κ²Œ μ΄λ£¨μ–΄μ§€λŠ”λ° κ·Έ λ°©μ‹μ—λŠ” 두가지가 μ‘΄μž¬ν•œλ‹€.

 

1. Simple request

λ³΄λ‚΄λŠ” 츑은 μš”μ²­μ˜ μ˜΅μ…˜μœΌλ‘œ credentals ν•­λͺ©μ„ true둜 ν•΄μ€€λ‹€.
λ°›λŠ” μΈ‘μ—μ„œλŠ” λͺ¨λ“  μ‚¬μ΄νŠΈλ₯Ό ν—ˆμš©ν•˜λŠ” μ™€μΌλ“œμΉ΄λ“œ(*)의 μ‚¬μš©μ„ μžμ œν•˜κ³  λ³΄λ‚΄λŠ” 츑의 좜처λ₯Ό μ •ν™•ν•˜κ²Œ λͺ…μ‹œν•΄μ•Ό ν•œλ‹€.
Simple requestλŠ” HTTP λ©”μ„œλ“œκ°€ GET, POST, HEAD인 일정 μ‘°κ±΄μ—μ„œ κ°€λŠ₯ν•œ 방식이닀.


2. Preflight

PUT, DELETE λ“± 데이터λ₯Ό μ‘°μž‘ν•˜λŠ” λ‹€λ₯Έ μš”μ²­λ“€μ€ μš”μ²­μ „μ— Preflight μš”μ²­μ„ μ„ ν–‰ν•˜λŠ”λ° μ΄λŠ” ν•΄λ‹Ή μš”μ²­μ΄ μ•ˆμ „ν•œμ§€ ν™•μΈν•˜λŠ” μš”μ²­μ΄λ‹€. Preflight μš”μ²­μ˜ μ‘λ‹΅μœΌλ‘œ ν—ˆλ½μ΄ λ–¨μ–΄μ§€λ©΄ κ·Έλ•Œμ„œμ•Ό λ³Έ μš”μ²­μ΄ 전솑이 λœλ‹€.
Simple request와 κ°€μž₯ 큰 차이점은 Preflight은 μš”μ²­μ„ ν•˜λŠ”λ°λ„ ν—ˆλ½μ„ λ°›μ•„μ•Ό μš”μ²­μ„ ν•  수 μžˆλ‹€λŠ” 점이 μžˆλ‹€.

 

κ²°κ΅­ CORSλŠ” λ‹€λ₯Έ 좜처(μ›Ήμ‚¬μ΄νŠΈ)κ°„μ˜ λ¦¬μ†ŒμŠ€(데이터) κ³΅μœ κ°€ κ°€λŠ₯ν•˜λ„λ‘ ν•œλ‹€. μš”μ²­μ„ λ°›λŠ” λ°±μ—”λ“œμ—μ„œ ν—ˆλ½ν•  λ‹€λ₯Έ μΆœμ²˜λ“€μ„ λͺ…μ‹œ(Access-Control-Allow-Origin)해두면 λ¦¬μ†ŒμŠ€ κ³΅μœ κ°€ κ°€λŠ₯ν•˜λ„λ‘ λ§Œλ“€μ–΄μ§„ λ©”μ»€λ‹ˆμ¦˜μ΄λ‹€.

 

πŸ“— Express CORS

React + Express둜 ν”„λ‘œμ νŠΈλ₯Ό μ§„ν–‰ν•˜λ©΄μ„œ CORSμ—λŸ¬λ₯Ό λ§ˆμ£Όν–ˆλŠ”λ° cors 라이브러리λ₯Ό μ΄μš©ν•΄μ„œ λ‹€μŒκ³Ό 같이 ν•΄κ²°ν–ˆλ‹€.

const app: Express = express();

app.use(
  cors({
    origin: true,  // *도 같은 의미 
    credentials: true,
  })
);

μœ„ μ½”λ“œλŠ” λͺ¨λ“  도메인에 λŒ€ν•΄μ„œ ν—ˆμš©ν•˜λŠ” μ½”λ“œμΈλ° λ§Œμ•½ νŠΉμ • 도메인에 λŒ€ν•΄μ„œ ν—ˆμš©ν•˜λ €λ©΄ origin에 ν—ˆμš©ν•˜λ €λŠ” 도메인을 μž…λ ₯ν•΄μ£Όλ©΄ λœλ‹€.

 

credentials: true λΌλŠ” μ˜΅μ…˜μ€ λ‹€λ₯Έ λ„메인 κ°„에 μΏ ν‚€ κ³΅μœ λ₯Ό ν—ˆλ½ν•˜λŠ” μ˜΅μ…˜μ΄λ‹€. 이와 κ΄€λ ¨ν•΄μ„œ ν”„λ‘ λ“œλ‹¨μ—μ„œ μΏ κΈ°λ₯Ό μ „μ†‘ν•΄μ•Όν•˜λŠ” 경우 withCredentials: trueλ₯Ό μ˜΅μ…˜μœΌλ‘œ λͺ…μ‹œν•΄μ€˜μ•Ό ν•œλ‹€. 

const { status } = await axios({
  method: λ©”μ„œλ“œ λͺ…,
  url: API μ£Όμ†Œ,
  data: {보내쀄 κ°’},
  withCredentials: true,
});