[TIL] Day36 #CORS

Beanxxยท2022๋…„ 6์›” 16์ผ
0

TIL

๋ชฉ๋ก ๋ณด๊ธฐ
36/120
post-thumbnail

2022.06.16(Thurs)

[TIL] Day36
[SEB FE] Day36

โ˜‘๏ธย CORS

Cross-Origin Resource Sharing (๊ต์ฐจ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ )
์ถ”๊ฐ€ HTTP ํ—ค๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•œ ์ถœ์ฒ˜์—์„œ ์‹คํ–‰ ์ค‘์ธ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ์„ ํƒํ•œ ์ž์›์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๋„๋ก ๋ธŒ๋ผ์šฐ์ €์— ์•Œ๋ ค์ฃผ๋Š” ์ฒด์ œ

๐Ÿ‘‰ย SOP์— ์˜ํ•ด ๋‹ค๋ฅธ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ ๋ฅผ ๋ง‰์€ ๋ธŒ๋ผ์šฐ์ €๋ฅผ CORS๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์‹œ ์ ‘๊ทผ ๊ถŒํ•œ์„ ์–ป๊ฒŒ ๋˜๋Š” ๊ฒƒ!

๐Ÿ“Žย SOP

Same-Origin Policy, ๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…
๐Ÿ‘‰ย ๊ฐ™์€ ์ถœ์ฒ˜์˜ ๋ฆฌ์†Œ์Šค๋งŒ ๊ณต์œ  ๊ฐ€๋Šฅ

๐Ÿซถ ์ถœ์ฒ˜(Origin): protocol, host, port ์กฐํ•ฉ (ex.https://www.google.com:443/)

๐Ÿคทโ€โ™€๏ธย Why SOP ์‚ฌ์šฉ?

๐Ÿ‘‰ย ์ž ์žฌ์ ์œผ๋กœ ํ•ด๋กœ์šธ ์ˆ˜ ์žˆ๋Š” ๋ฌธ์„œ๋ฅผ ๋ถ„๋ฆฌํ•จ์œผ๋กœ์จ ๊ณต๊ฒฉ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ๊ฒฝ๋กœ๋ฅผ ์ค„์—ฌ์ฃผ๋ฏ€๋กœ

  • http://google.com:81 vs http://google.com โ†’ http ํ”„๋กœํ† ์ฝœ ๊ธฐ๋ณธ ํฌํŠธ๋Š” 80์ด๋ฏ€๋กœ ๋‘ URL์€ ๋™์ผ ์ถœ์ฒ˜ โŒ
  • https://google.com:443 vs https://google.com โ†’ https ํ”„๋กœํ† ์ฝœ ๊ธฐ๋ณธ ํฌํŠธ๋Š” 443์ด๋ฏ€๋กœ ๋‘ URL์€ ๋™์ผ ์ถœ์ฒ˜ โญ•๏ธ

โœ…ย ๋‹ค๋ฅธ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ์‹ถ์ง€๋งŒ SOP ๋–„๋ฌธ์— ์ ‘๊ทผ โŒ
โ†’ CORS ์„ค์ •์œผ๋กœ ์„œ๋ฒ„ ์‘๋‹ต ํ—ค๋”์— โ€˜Access-Control-Allow-Originโ€™์„ ์ž‘์„ฑํ•˜๋ฉด ์ ‘๊ทผ ๊ถŒํ•œ ํš๋“ ๊ฐ€๋Šฅ

์ „์— Three.js ํ† ์ด ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ–ˆ์„ ๋•Œ ์•„๋ž˜์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋– ์„œ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ฐ”๋กœ ์‹คํ–‰์‹œํ‚ค์ง€ ๋ชปํ•˜๊ณ , http-server๋ฅผ npm์œผ๋กœ ์„ค์น˜ํ•œ ํ›„์— ์ด๋ฅผ ์ด์šฉํ–ˆ์—ˆ๋Š”๋ฐ ์ด๋ฒˆ ์‹œ๊ฐ„์„ ํ†ตํ•ด CORS๊ฐ€ ๋ฌด์—‡์ด๊ณ , ์™œ ์ด๋Ÿฐ error๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”์ง€ ์กฐ๊ธˆ์ด๋‚˜๋งˆ ์•Œ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2022-06-12 แ„‹แ…ฉแ„Œแ…ฅแ†ซ 2 42 34

๐Ÿ“Žย CORS ๋™์ž‘ ๋ฐฉ์‹

1. Preflight Request

์‹ค์ œ ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์ „, OPTIONS ๋ฉ”์„œ๋“œ๋กœ ์‚ฌ์ „ ์š”์ฒญ์„ ๋ณด๋‚ด, ํ•ด๋‹น ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผ ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€๋ถ€ํ„ฐ ํ™•์ธ

  • ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ , ์‘๋‹ต ํ—ค๋”์˜ Access-Control-Allow-Origin์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ธ ์ถœ์ฒ˜๊ฐ€ ๋Œ์•„์˜ค๋ฉด ์‹ค์ œ ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋จ
  • ์š”์ฒญ์„ ๋ณด๋‚ธ ์ถœ์ฒ˜๊ฐ€ ์ ‘๊ทผ ๊ถŒํ•œ์ด ์—†๋‹ค๋ฉด ๋ธŒ๋ผ์šฐ์ €์—์„œ CORS ์—๋Ÿฌ, ์‹ค์ œ ์š”์ฒญ ์ „๋‹ฌ โŒ

๐Ÿ‘ย ์‹ค์ œ ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์ „ ๋ฏธ๋ฆฌ ๊ถŒํ•œ ํ™•์ธ ๊ฐ€๋Šฅ โ†’ ๊ถŒํ•œ ์—†๋Š” ์š”์ฒญ์„ ๋ฏธ๋ฆฌ ๊ฑฐ๋ฅผ ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ 

 if (request.method === "OPTIONS") {
     response.writeHead(200, defaultCorsHeader);
     response.end();
  }

2. Simple Request

ํŠน์ • ์กฐ๊ฑด์ด ๋งŒ์กฑ๋˜๋ฉด Preflight ์š”์ฒญ์„ ์ƒ๋žตํ•˜๊ณ  ์š”์ฒญ์„ ๋ณด๋ƒ„

a. GET, HEAD, POST ์š”์ฒญ ์ค‘ ํ•˜๋‚˜์—ฌ์•ผ ํ•จ
b. Accept, Accept-Language, Content-Language, Content-Type ํ—ค๋” ๊ฐ’๋งŒ ์ˆ˜๋™์œผ๋กœ ์„ค์ • ๊ฐ€๋Šฅ
ย ย ย ย  i. Content-Type ํ—ค๋”์—” application/x-www-form-urlencoded, multipart/form-data, text/plain ๊ฐ’๋งŒ ํ—ˆ์šฉ

3. Credentialed Request(์ธ์ฆ์ •๋ณด๋ฅผ ํฌํ•จํ•œ ์š”์ฒญ)

์š”์ฒญ ํ—ค๋”์— ์ธ์ฆ ์ •๋ณด๋ฅผ ๋‹ด์•„ ๋ณด๋‚ด๋Š” ์š”์ฒญ

๋ณ„๋„์˜ ์„ค์ •์„ ํ•˜์ง€ ์•Š์œผ๋ฉด ์ฟ ํ‚ค๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์—†์Œ (๐Ÿคทโ€โ™€๏ธwhy? ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ผ์„œ)
โ†’ Front, Server ๋ชจ๋‘ CORS ์„ค์ • ํ•„์š”

  • Front ์ธก: ์š”์ฒญ ํ—ค๋”์— withCredentials: true ์‚ฝ์ž…
  • Server ์ธก: ์‘๋‹ต ํ—ค๋”์— Access-Control-Allow-Credentials: true ์‚ฝ์ž…
  • Server ์ธก์—์„œ Access-Control-Allow-Origin ์„ค์ •์‹œ,
    ๋ชจ๋“  ์ถœ์ฒ˜ ํ—ˆ์šฉ ์˜๋ฏธ์˜ ์™€์ผ๋“œ์นด๋“œ(*)๋กœ ์„ค์ •ํ•˜๋ฉด Error ๋ฐœ์ƒ ๐Ÿ‘‰ย ์ถœ์ฒ˜ ์ •ํ™•ํžˆ ์„ค์ •!

๐Ÿ“Žย CORS ์„ค์ • ๋ฐฉ๋ฒ•

  1. Node.js Server

    const http = require('http');
    
    const server = http.createServer((request, response) => {
    	// all domain
    	response.setHeader("Access-Control-Allow-Origin", "*");
    
    	// ํŠน์ • domain
    	response.setHeader("Access-Control-Allow-Origin", "https://google.com");
    
    	// ์ธ์ฆ ์ •๋ณด๋ฅผ ํฌํ•จํ•œ ์š”์ฒญ์„ ๋ฐ›์„ ๊ฒฝ์šฐ
    	response.setHeader("Access-Control-Allow-Credentials", "true");
    })
  1. Express Server

    // cors ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋” ๊ฐ„๋‹จํžˆ CORS ์„ค์ • ๊ฐ€๋Šฅ
    const cors = require("cors");
    const app = express();
    
    // all domain
    app.use(cors());
    
    // ํŠน์ • domain
    const options = {
    	origin: "https://google.com", // ์ ‘๊ทผ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๋Š” domain
    	credentials: true, // ์‘๋‹ต ํ—ค๋”์— Access-Control-Allow-Credentials ์ถ”๊ฐ€
    	optionsSuccessStatus: 200, // ์‘๋‹ต ์ƒํƒœ 200 ์„ค์ •
    };
    
    app.use(cors(options));
    
    // ํŠน์ • Request
    app.get("/example/:id", cors(), function (req, res, next) {
    	res.json({ msg: "example" });
    });


โ˜‘๏ธย Mini Node Server

Node.js์˜ http module์„ ์ด์šฉํ•˜์—ฌ Web Server ๋งŒ๋“ค๊ธฐ

  • Client Action(button click)์— ๋”ฐ๋ผ ๋‹ค๋ฅธ HTTP ์š”์ฒญ์„ ์„œ๋ฒ„๋กœ ๋ณด๋‚ด๊ณ ,
    HTTP ์š”์ฒญ์— ๋‹ด์•„ ๋ณด๋‚ธ ๋‹จ์–ด๋ฅผ ์†Œ๋ฌธ์ž/๋Œ€๋ฌธ์ž๋กœ ์‘๋‹ต ๋ฐ›์•„ ํ™”๋ฉด์— ๋ณด์—ฌ์ฃผ๋Š” ๊ธฐ๋Šฅ ๊ตฌํ˜„

โžฐย Web Server: HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‘๋‹ต์„ ๋ณด๋‚ด์ฃผ๋Š” ํ”„๋กœ๊ทธ๋žจ

// Server ์‹คํ–‰
node [folder name]/[file name]

// nodemon ์‚ฌ์šฉ์‹œ, ์„œ๋ฒ„๋ฅผ ๋งค๋ฒˆ ์‹คํ–‰์‹œํ‚ฌ ํ•„์š”์—†์Œ
// 1-1. nodemon ์„ค์น˜
npm install nodemon

// 1-2. package.json์˜ 'scripts' ์ฝ”๋“œ ์ถ”๊ฐ€
"start": "nodemon [folder name]/[file name]"

// 2. nodemon์„ ๋”ฐ๋กœ ์„ค์น˜ํ•˜์ง€ ์•Š๊ณ  ์‹คํ–‰
npx nodemon [folder name]/[file name]
// Client ์‹คํ–‰
// 1. web browser์—์„œ html ํŒŒ์ผ ์‹คํ–‰
// 2. ํŠน์ • port๋กœ client ์‹คํ–‰ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, serve ์ด์šฉ
npx serve -l [port number] [folder name]/
// server/basic-server.js

const http = require("http");

const PORT = 4999;
const ip = "localhost";

// ์„œ๋ฒ„ ์ƒ์„ฑ
const server = http.createServer((request, response) => {

	// preflight request   
	if (request.method === "OPTIONS") {
		// ๋ช…์‹œ์ ์œผ๋กœ response stream์— Header ์ž‘์„ฑ
		// writeHead() ๋ฉ”์„œ๋“œ์—” status code, Header ์ž‘์„ฑ
		// ํ—ค๋”๋ฅผ ์„ค์ •ํ•˜๊ณ  ๋‚˜๋ฉด ์‘๋‹ต ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•  ์ค€๋น„ ์™„๋ฃŒ!
    response.writeHead(200, defaultCorsHeader);
    response.end();
    return; // ์กฐ๊ฑด๋ฌธ๋งˆ๋‹ค return; ์•ˆ ํ•ด์ฃผ๋ฉด app crushed ์–ด์ฉŒ๊ตฌ ์—๋Ÿฌ ๋ฐœ์ƒ
  }

  let body = [];
	// ๋ฌธ์ž์—ด์„ ์†Œ๋ฌธ์ž๋กœ ๋งŒ๋“ค์–ด ์‘๋‹ต
	// ์š”์ฒญ ๋ฉ”์„œ๋“œ๊ฐ€ 'POST'์ด๋ฉด์„œ Endpoint(URL)๊ฐ€ /lower์ธ ๊ฒฝ์šฐ
  if (request.method === "POST" && request.url === "/lower") {
    request
			// ๊ฐ 'data' event์—์„œ ๋ฐœ์ƒ์‹œํ‚จ chunk๋Š” 'Buffer'.
			// chunk๊ฐ€ ๋ฌธ์ž์—ด ๋ฐ์ดํ„ฐ๋ผ๋ฉด ์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฐ์—ด์— ์ˆ˜์ง‘ํ•œ ํ›„,
			// 'end' ์ด๋ฒคํŠธ์— ์ด์–ด ๋ถ™์—ฌ ๋‹ค์Œ ๋ฌธ์ž์—ด๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด good!
      .on("data", (chunk) => {
        body.push(chunk);
      })
      .on("end", () => {
				// 'body'์— ์ „์ฒด ์š”์ฒญ ๋ฐ”๋””๊ฐ€ ๋ฌธ์ž์—ด๋กœ ๋‹ด๊ฒจ ์žˆ์Œ
        body = Buffer.concat(body).toString();
        response.writeHead(200, defaultCorsHeader);
        response.end(body.toLowerCase());
        return;
      });
	// ๋ฌธ์ž์—ด์„ ๋Œ€๋ฌธ์ž๋กœ ๋งŒ๋“ค์–ด ์‘๋‹ต
	// ์š”์ฒญ ๋ฉ”์„œ๋“œ๊ฐ€ 'POST'์ด๋ฉด์„œ Endpoint(URL)๊ฐ€ /upper์ธ ๊ฒฝ์šฐ
  } else if (request.method === "POST" && request.url === "/upper") {
    request
      .on("data", (chunk) => {
        body.push(chunk);
      })
      .on("end", () => {
        body = Buffer.concat(body).toString();
        response.writeHead(200, defaultCorsHeader);
        response.end(body.toUpperCase());
        return;
      });
	// ์œ„ ์กฐ๊ฑด์ด ์•„๋‹Œ ๊ฒฝ์šฐ 404 ์‘๋‹ต
  } else {
    response.statusCode = 404;
    response.end();
  }
});

// listen ๋ฉ”์„œ๋“œ๋ฅผ server ๊ฐ์ฒด์—์„œ ํ˜ธ์ถœํ•จ์œผ๋กœ์จ ์š”์ฒญ์„ ์‹ค์ œ๋กœ ์ฒ˜๋ฆฌ
// ๋Œ€๋ถ€๋ถ„ ์„œ๋ฒ„๊ฐ€ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” ํฌํŠธ ๋ฒˆํ˜ธ๋ฅผ listen์— ์ „๋‹ฌ
server.listen(PORT, ip, () => {
  console.log(`http server listen on ${ip}:${PORT}`);
});

const defaultCorsHeader = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
  "Access-Control-Allow-Headers": "Content-Type, Accept",
  "Access-Control-Max-Age": 10,
};

โœ‹ npm run start๋กœ ์„œ๋ฒ„๋„ ์‹คํ–‰์‹œํ‚ค๊ณ , ํด๋ผ์ด์–ธํŠธ๋„ ๋ธŒ๋ผ์šฐ์ €์— ํ•จ๊ป˜ ๋Œ๋ ค์•ผ ์ž‘๋™ ๊ฐ€๋Šฅํ•˜๋‹ค.

๐Ÿ€ย ์ฐธ๊ณ -Node ๋ฌธ์„œ
https://nodejs.org/ko/docs/guides/anatomy-of-an-http-transaction/

profile
FE developer

0๊ฐœ์˜ ๋Œ“๊ธ€