인프런 커뮤니티 질문&답변

챠챠_님의 프로필 이미지

작성한 질문수

Next + React Query로 SNS 서비스 만들기

Static(SSG) 모드와 사라진 ISR 모드

배포테스트할때 클라이언트에서 api호출하면 쿠키가 전달되지 않고있습니다.

해결된 질문

24.06.23 18:00 작성

·

420

·

수정됨

0

안녕하세요 선생님

배포테스트할때 클라이언트에서 api호출하면 쿠키가 전달되지 않고있습니다.

 

클라이언트에서는

credentials: 'include'를 적용했고

const response = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/user/setting`, {
        method: 'PATCH',
        credentials: 'include',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(userSettingObj),
      });
      if (response.ok) {
        const sessionUpdateInfo = await response.json();
        await updateSession(sessionUpdateInfo);
        router.push('/');
      }

 

서버에서는

const express = require('express');
const cors = require('cors');
const passport = require('passport');
const session = require('express-session');
const cookieParser = require('cookie-parser');
const dotenv = require('dotenv');
const morgan = require('morgan');
const path = require('path');
const hpp = require('hpp');
const helmet = require('helmet');

const postRouter = require('./routes/post');
const postsRouter = require('./routes/posts');
const userRouter = require('./routes/user');
const usersRouter = require('./routes/users');
const db = require('./models');
const passportConfig = require('./passport');

dotenv.config();
const app = express();
db.sequelize.sync()
  .then(() => { console.log('db 연결 성공') })
  .catch(console.error);
passportConfig();

if (process.env.NODE_ENV === 'production') {
  app.use(morgan('combined'));
  app.use(hpp());
  app.use(helmet());
} else {
  app.use(morgan('dev'));
}

app.use(
  cors({
    origin: ['http://localhost:3000', 'whatisyourmbti.com', 'http://43.201.56.221'], // true or * // access-control-allow-origin가 true된다. --> 다른 도메인끼리 api 요청
    credentials: true, // access-control-allow-credential가 true된다. --> 다른 도메인끼리 쿠키 전달
    method: '*',
  })
);
// 프론트에서 보낸 정보를 req.body에 넣어준다. 순서 중요!
app.use(express.json()); // json 형식으로 보냈을때 데이터 처리해줌
app.use(express.urlencoded({ extended: true })); // form submit으로 보냈을 때 데이터 처리해줌
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(session({
  saveUninitialized: false,
  resave: false,
  secret: process.env.COOKIE_SECRET,
  cookie: {
    httpOnly: true,
    secure: false,
  }
}));
app.use(passport.initialize());
app.use(passport.session());

app.use((req, res, next) => {
  console.log('Session ID:', req.sessionID);
  console.log('Cookies:', req.cookies);
  console.log('Signed Cookies:', req.signedCookies);
  next();
});

app.get('/', (req, res) => {
  res.send('hello express');
});

app.get('/api', (req, res) => {
  res.send('hello api');
});

app.use('/post', postRouter);
app.use('/posts', postsRouter);
app.use('/user', userRouter);
app.use('/users', usersRouter);

app.listen(80, () => {
  console.log('서버 실행 중!');
});

credentials: true, 쿠키옵션을 설정해주었는데

0|app | Session ID:---------------------------- U8DRuillNv2DBRmexmO1mZZ7fGJeWXCw

0|app | Cookies:------------------------------- [Object: null prototype] {}

0|app | Signed Cookies:------------------------ [Object: null prototype] {}
이런식으로 쿠키값이 전달되고 있지 않아서

유저 정보가 필요한(로그인 확인)로직에서 401에러가 떨어지게 됩니다.

app.use(session({
  saveUninitialized: false,
  resave: false,
  secret: process.env.COOKIE_SECRET,
  cookie: {
    httpOnly: true,
    secure: false,
  }
}));

위부분들을 검색해보고 바꿔보기도 했는데, 잘안되더라구요.

클라이언트의 ip는 http://43.201.56.221입니다.

 

쿠키는 로그인 후 프론트서버에서 브라우저에 삽입해주고 있습니다.

console.log(`${process.env.NEXT_PUBLIC_BASE_URL}/user/login ---------------------login api`);
          const authResponse = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/user/login`, 
            {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                ...credentials
              }),
            }
          );
          
          // 프론트서버에서 백엔드서버의 로그인 토큰을 받아온것. 토큰은 문자열이라서
          // cookie라이브러리로 객체로 만들어준다.
          let setCookie = authResponse.headers.get('Set-Cookie');

          console.log('set-cookie', setCookie);
          if (setCookie) {
            const parsed = cookie.parse(setCookie);
            console.log(parsed, '---------------parsed cookie');
            // 브론트서버에서 브라우저에 쿠키를 심어준다.
            // 프론트서버에 쿠키를 심으면 안된다! 왜냐하면 프론트서버는 서버라서 공용이다.
            // 여러 브라우저가 전부 프론트서버르 바라본다. 개인정보 유출 문제 발생할 수 있다.
            cookies().set('connect.sid', parsed['connect.sid'], parsed); // parsed = 나머지 옵션들
          }
          
          console.log(authResponse, '--------------------------------authResponse');
          let user = await authResponse.json();
          console.log(user, '--------------------------------user'); 
          // console.lo(authResponse);
          if (!authResponse.ok) {
            return null;
          }

          // return user object with the their profile data
          return {
            ...user,
            email: user.email,
            name: user.nickname,
            image: user.image,
            id: user.id,
          }
        } catch (err) {
          console.error('로그인 에러', err);
        } 

 

그리고 cors에러는 발생하지 않고있습니다.

 

혹시 수정해야할 코드나, 참고해야할 부분이 있다면 알려주시면 감사하겠습니다.

 

 

 

답변 2

0

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2024. 06. 24. 00:17

프론트 도메인과 서버 도메인이 서로 다르면 쿠키 전달이 매우 어렵습니다. 다른 상황인가요?

챠챠_님의 프로필 이미지
챠챠_
질문자

2024. 06. 24. 17:52

네 선생님

기존에는 도메인이 달랐고 탄력적 IP로 바꾼다음에는

프론트: xxx.com

백엔드: api.xxx.com 으로 도메인 맞춰서 작업했습니다.

그럼에도 incredentials만으로는 쿠키전달이 안되서

다른 비슷한 질문과 구글링해보고

클라이언트에서는 api router이용해서 서버함수를 실행하고 서버함수에서 쿠키를 가져와서 전달하면 작동한다는 글을 보고 따라해보니 되더라구요.

 

그래서 다른 api호출도 다 이런 방식으로 바궈야 401에러가 안날것 같은데 이런 방식으로 진행해도 될지 궁금합니다.

예: 클라이언트에서 /api/user/make 호출 -->
api/user/make/route.ts에서 서버함수 호출 --> 이후 데이터 리턴

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2024. 06. 24. 17:59

도메인 맞추고 cookie의 Domain도 .xxx.com 이었나요? (앞에 점 붙어야함) 그래야 백엔드와 전달됩니다.

챠챠_님의 프로필 이미지
챠챠_
질문자

2024. 06. 24. 18:22

넵그렇습니다.

서버 도메인도 맞췄었습니다.

app.use(session({
  saveUninitialized: false,
  resave: false,
  secret: process.env.COOKIE_SECRET,
  cookie: {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production', // 프로덕션 환경에서만 secure 적용
    sameSite: process.env.NODE_ENV === 'production' ? 'None' : 'Lax', // 크로스 도메인 설정
    domain: process.env.NODE_ENV === 'production' ? '.zzimzzim.com' : undefined // 프로덕션 도메인 설정
  }
}));

 

이렇게 하고 클라이언트에선 요청할데 크리덴셜스 추가해서 했었습니다.

그럼에도 쿠키가 전달안됐어서

지금은

프론트: 로컬,

백엔드: api.xxx.com
에서 테스트 하고있으며

https://www.inflearn.com/questions/1106691/api-%EC%9A%94%EC%B2%AD-%EC%8B%9C-cookie-%EB%B3%B4%EB%82%B4%EB%8A%94-%EA%B2%83%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A7%88%EB%AC%B8

이 답변을 참고해서 진행했었습니다!

 

 

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2024. 06. 24. 19:15

지금 https는 적용하셨나요? 그리고 로그인 시 쿠키가 네트워크 탭에 보입니다. 거기서 Domain, Secure 이런 거 다 맞는지 확인하세요. 쿠키 문제는 무조건 개인의 실수일 수밖에 없습니다. 네트워크 탭과 비교해보시면 됩니다.

챠챠_님의 프로필 이미지
챠챠_
질문자

2024. 06. 24. 21:37

https 적용전입니다.

 

실수하지 않았다면 credentials 옵션만으로 쿠키 공유가 되야한다는 뜻일까요?

 

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2024. 06. 24. 21:57

네네 심지어 네트워크탭에 왜 쿠키가 안 심어지는지 이유까지 다 나옵니다

챠챠_님의 프로필 이미지
챠챠_
질문자

2024. 06. 24. 22:30

그렇다면 https://www.inflearn.com/questions/1106691/api-%EC%9A%94%EC%B2%AD-%EC%8B%9C-cookie-%EB%B3%B4%EB%82%B4%EB%8A%94-%EA%B2%83%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A7%88%EB%AC%B8
여기에 답변해주신 내용은
프론트: 로컬,

백엔드: ec2서버로
도메인이 달라서 해주신 답변이고,

지금 저와같이 도메인이 같게 한 상태라면 credentials 옵션맞으로 되야하는거군요... 혹시 https작업하지 않아서 발생할수도 있는걸까요? 아무리해봐도 안되서 위위 같이 돌아간건데 좀더 작업해보면서 찾아보도록 하겠습니다.

정말 감사합니다.

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2024. 06. 25. 01:59

아뇨 https 없으면 secure만 false면 됩니다. 네트워크탭 response cookie탭 봐보세요

챠챠_님의 프로필 이미지
챠챠_
질문자

2024. 06. 25. 11:27

선생님 바쁘실텐데 소중한 답변 감사합니다.

그리고 죄송합니다

제가 정말 바보같은 실수를 했었더라구요..

제가 이 강의랑 넥스트강의를 같이 보면서 작업을 했는데 넥스트 강의에서 쿠키를 심어줄때 그냥 쿠키만 심어주고나서 잊고있다가 이번에 선생님이 답변주신 네트워크 > 쿠키탭을 보고 이상하다여겨서 봤더니 쿠키심을때 따로 값을 안주고 그냥 심기만해서 발생한 문제였습니다... ㅠ

 

담부턴 좀 더 살피고 질문할 수 있도록 하겠습니다.

감사합니다.

0

인프런 AI 인턴님의 프로필 이미지

2024. 06. 23. 18:06

안녕하세요, 인프런 AI 인턴입니다.

챠챠_님의 문제는 클라이언트에서 API 호출 시 쿠키가 제대로 전달되지 않아 발생하는 것으로 보입니다. 이 문제는 특히 프로덕션 환경에서 CORS(Cross-Origin Resource Sharing) 설정과 쿠키 설정에 따라서 발생할 수 있습니다. 쿠키가 브라우저에 저장되지 않거나, 서버에 전달되지 않는 경우가 주 원인일 수 있습니다.

문제 해결을 위해 확인해야 할 핵심 포인트는 다음과 같습니다:
1. CORS 설정: Access-Control-Allow-Credentials 헤더와 Access-Control-Allow-Origin 헤더 설정이 브라우저에 제대로 전달되고 있는지 확인하세요. credentials: true는 올바르게 설정하셨으나, origin 설정에서 모든 클라이언트의 URL이 정확하게 매칭되는지 확인해야 합니다. origin: true를 사용하면 요청의 origin 헤더값을 그대로 반환해주어 동적으로 허용할 수 있으나, 보안 상의 이유로 실제 프로덕션에서는 사용할 때 주의가 필요합니다.

  1. 쿠키 설정: 쿠키의 도메인과 경로(Path) 설정을 확인하세요. 도메인이 설정되지 않았거나 경로가 잘못 설정된 경우 쿠키가 저장되지 않을 수 있습니다. 프론트엔드와 백엔드가 다른 도메인에서 서비스되고 있다면 쿠키의 Domain 속성을 적절히 설정해야 합니다.

  2. HTTPS 설정: 프로덕션 환경에서는 Secure 쿠키 옵션과 HTTPS를 사용하는 것이 일반적입니다. 현재 secure: false로 되어 있는데, 프로덕션 환경에서 HTTPS를 사용한다면 secure: true로 변경해야 쿠키가 제대로 전달됩니다.

관련 질문 & 답변 링크

당면한 문제 해결을 위한 제안:

  • 클라이언트와 API 서버 간의 도메인이 다르다면, 정확한 도메인 또는 IP를 origin 배열에 포함시키고, 서버와 클라이언트 간의 쿠키 공유를 위해 cookie 설정에서 Domain 속성을 올바르게 설정해야 할 수 있습니다.
  • 프로덕션 환경에서 HTTPS를 사용 중이라면 secure 쿠키 옵션을 적극 고려해 보세요.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

챠챠_님의 프로필 이미지

작성한 질문수

질문하기