Language/Nodejs

[Nodejs] RestAPI 서버를 만들었는데 TypeError: Failed to fetch 에러

멱군 2023. 4. 18. 18:49

앞선 포스트에서 Node.js를 사용하여 RESTful API 서버를 만들었으나, 클라이언트에서 요청을 보낼 때 "TypeError: Failed to fetch" 오류를 만났다. 또한 "Uncaught (in promise) TypeError: Failed to fetch" 오류도 발생하였다. 이러한 오류들의 주된 원인은 CORS(Cross-Origin Resource Sharing)에 관련된 문제이다.

 

 

1. TypeError: Failed to fetch 에러 발생

앞선 포스트에서 Node.js를 사용하여 RESTful API 서버를 만글고, 클라이언트에서 값을 입력해서 진행을 했는데 진행이 안되서 디버깅 창을 켜보니 TypeError: Failed to fetch 와 같은 에러가 발생하면서 동작이 진행되지 않는다.

이러한 오류들의 주된 원인은 CORS(Cross-Origin Resource Sharing) 때문이다. CORS 오류는 클라이언트와 서버 간의 리소스 공유 정책과 관련이 있다. 주로, 서로 다른 도메인 또는 포트 간의 요청에 발생한다. 이러한 오류를 해결하려면 서버 측에서 특정 도메인이나 모든 도메인을 허용하는 설정을 추가해야 한다.

 

 

2. 에러발생 원인

이러한 에러가 뜨는데 Uncaught (in promise) TypeError: Failed to fetch 오류는 클라이언트에서 서버에 요청을 보낼 때 발생할 수 있는 일반적인 오류로 이 오류의 원인은 주로 다음과 같다.

  1. 서버가 실행되지 않거나 올바른 포트 번호로 실행되지 않음
  2. 클라이언트에서 요청하는 API 엔드포인트가 존재하지 않음
  3. CORS(Cross-Origin Resource Sharing) 문제
  4. 클라이언트에서 요청하는 API URL이 잘못되었거나 오타가 있음

원인을 다음의 점검사항으로 찾아볼수 있다.

  • 서버가 실행 중인지 확인
  • 올바른 포트 번호로 실행되고 있는지 확인
  • 기본적으로 서버는 http://localhost:3000에서 실행되어야 함
  • 클라이언트에서 요청하는 API 엔드포인트가 서버에서 구현되었는지 확인
    • 예를 들어, /api/users와 같은 엔드포인트가 서버에서 구현되어야 함

주소도 맞고 소스상으로서도 요청은 잘하고 있다.

Access to fetch at 'http://localhost:3000/api/users' 
from origin 'http://localhost:3001' has been blocked by CORS policy: 
Response to preflight request doesn't pass access control check: 
No 'Access-Control-Allow-Origin' header is present on the requested resource. 
If an opaque response serves your needs, 
set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

메인 에러는 이부분인데 클라이언트와 서버 간의 CORS(Cross-Origin Resource Sharing) 정책 때문에 발생하는 에러이다. 클라이언트가 다른 포트에서 실행 중인 서버에 요청을 보낼 때 발생할 수 있기 때문에 이 문제를 해결하려면 서버에서 CORS를 허용해야 한다.

그러므로 원인은 3번인듯, 이 문제를 해결하려면 다음처럼 해결할 수 있다.

서버에서 CORS 설정을 추가해야 한다.

Express.js에서 CORS 설정을 추가하려면 다음 단계를 실행하면 된다.

 

3. CORS 오류를 해결하는 방법

1. cors 패키지를 설치

npm install cors

 

2. index.js 파일에서 CORS 미들웨어를 추가

const cors = require("cors");

// ...

app.use(cors());

서버는 모든 도메인 및 포트에서 요청을 수락해야 하고, 프로덕션 환경에서는 보안상의 이유로 특정 도메인 또는 포트만 허용하도록 CORS 설정을 조정하는 것이 좋다. 

 

3. CORS 설정

const cors = require("cors");

// CORS 설정
const corsOptions = {
  origin: ["http://localhost:3001"], // 허용하려는 도메인 목록
  methods: ["GET", "POST", "PUT", "DELETE"], // 허용하려는 HTTP 메서드
  allowedHeaders: ["Content-Type", "Authorization"], // 허용하려는 헤더
};

// ...

app.use(cors(corsOptions));

이렇게 설정하면 지정한 도메인을 허용하는데, 만약 여러개를 허용한다면,

origin: ["http://localhost:3001", "http://example.com"]

처럼 배열로 여러개를 넣어서 진행하면 된다.

앞에서 만들었던 RESTful API 서버에 이 변경 사항을 적용한 후, 웹 서버를 재시작하여 CORS 오류가 해결되었는지 확인한다. 이렇게 설정을 변경하면, 클라이언트와 서버 간의 통신이 원활하게 진행되며 CORS 문제를 쉽게 해결할 수 있다.


만약 모든 도메인을 허용하려면, cors 미들웨어에 origin 옵션을 true로 설정하거나, origin 옵션을 전혀 사용하지 않으면 된다.

const cors = require("cors");

// CORS 설정
const corsOptions = {
  origin: true, // 모든 도메인 허용
  methods: ["GET", "POST", "PUT", "DELETE"], // 허용하려는 HTTP 메서드
  allowedHeaders: ["Content-Type", "Authorization"], // 허용하려는 헤더
};

// ...

app.use(cors(corsOptions));

하거나,

const cors = require("cors");

// ...

app.use(cors());

로 진행하면 모든 도메인을 허용하는데 이는 보안상의 이슈가 발생할 수 있다.

프로덕션 환경에서는 신뢰할 수 있는 특정 도메인만 허용하는 것이 좋으며, 테스트나 개발 단계에서만 모든 도메인을 허용하는게 좋다.


전체소스코드

앞에서 만들었던 RESTful API Server의 수정한 전체 코드이다.

const express = require('express');
const bodyParser = require('body-parser');
const cors = require("cors");

const app = express();
const port = process.env.PORT || 3000;

// app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

//CORS 설정
const corsOptions = {
    // 허용하려는 도메인
    origin: [   
                "http://localhost:3001", 
            ],
    // 허용하려는 HTTP 메서드
    methods: ["GET", "POST", "PUT", "DELETE"], 
    // 허용하려는 헤더
    allowedHeaders: ["Content-Type", "Authorization"], 
  };

app.use(cors(corsOptions));

app.get('/', (req, res) => {
  res.send('Welcome to the RESTful API server!');
});

let users = []; // In-memory storage for simplicity
let currentId = 1;

// Create (POST)
app.post("/api/users", (req, res) => {
  const { name, email } = req.body;
  const newUser = { id: currentId++, name, email };
  users.push(newUser);
  res.status(201).json(newUser);
});

// Read (GET)
app.get("/api/users", (req, res) => {
  res.json(users);
});

app.get("/api/users/:id", (req, res) => {
  const user = users.find((u) => u.id === parseInt(req.params.id));
  if (user) {
    res.json(user);
  } else {
    res.status(404).send("User not found");
  }
});

// Update (PUT)
app.put("/api/users/:id", (req, res) => {
  const { name, email } = req.body;
  const user = users.find((u) => u.id === parseInt(req.params.id));
  if (user) {
    user.name = name;
    user.email = email;
    res.json(user);
  } else {
    res.status(404).send("User not found");
  }
});

// Delete (DELETE)
app.delete("/api/users/:id", (req, res) => {
  const userIndex = users.findIndex((u) => u.id === parseInt(req.params.id));
  if (userIndex !== -1) {
    users.splice(userIndex, 1);
    res.status(204).send();
  } else {
    res.status(404).send("User not found");
  }
});  

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

 

수정 된 index.js 파일을 압축하여 첨부한다.

index.zip
0.00MB

 

함께보면 좋은글

 

[Nodejs] RESTful API Client 만들기 서버와 통신하기

이전 글에서는 Nodejs를 사용하여 RESTful API 서버를 만드는 방법에 대해 알아보았다. 이번 글에서는 해당 API 서버와 통신하는 클라이언트 서버를 구축하는 방법을 살펴볼 것이다. 이 클라이언트는

devit.koreacreatorfesta.com

 

CORS는 왜 사이트를 만들 때 어렵게 만드는거야?

CORS(Cross-Origin Resource Sharing)는 웹 개발자들 사이에서 빈번하게 논의되는 주제입니다. 하지만 웹사이트 개발에 있어서 필수 보안 정책입니다. 웹사이트를 만들 때 마주치는 다양한 도전 중 하나인

devit.koreacreatorfesta.com