개발자

토큰값을 받아오지 못하는 문제 (undefined)

2024년 04월 06일조회 159

안녕하세요 JWT 토큰을 이용해 로그인 검증하는 로직을 구현하고 있습니다. 로컬스토리지에 토큰을 저장하는 것까지 성공했으나 프론트 측에서 로그인 검증을 요청할때 실제 토큰 값을 받아오지 못하는 문제가 있어 질문 드립니다. login-required에서 토큰 값 유무 로직을 전부 지우고 콘솔로만 찍는 경우 undefined와 실제 토큰값이 나타납니다. 만약 토큰 값 유무 로직을 이용한다면 undefined값만 서버로 들어와 페이지가 무한로딩 되는 현상이 나타납니다. next()처리가 안되기 때문인 것 같습니다. (+ index.html은 test.js를 연결하고 있습니다.) 사진으로 보면 네트워크 요청헤더에서 확인한 결과 1. 처음엔 document 유형의 localhost를, (인증 헤더 x) 이후 js를 불러온다 2. 마지막에 xhr형식인 localhost를 불러옵니다 (인증 헤더 o) (여기서 undefined, 실제 토큰값이 콘솔에 동시에 나타나는 이유라고 생각이 듦) (순서를 보면 document 유형 localhost -> css, js -> xhr 유형 localhost 즉 처음에 document 유형에서 인증헤더를 못가져와서 (js가 로드되기 전이여서) 토큰 유무를 체크하지 못하는 것 같습니다) 결론적으로 제가 생각한 문제 분석 (1) ('/') 에 get 요청을 보낸다. (2) 서버는 loginRequired를 실행한다. (3) 서버로 부터 html을 받지 못한 상태에 loginRequired가 실행되어 토큰 값은 undefined가 된다. (4) next()를 거쳐 서버에서 html을 클라이언트로 부터 응답을 해준다. (5) html을 브라우저에 보여주면서 그 html에 연결된 js를 불러온다. (6) 이때 불러온 test.js에서 get요청 ('/') 로직을 불러와 다시 server.js에서 loginRequired 응답으로 토큰 값을 불러온다. 즉 js가 로드가 되어야 userToken을 로컬스토리지로 부터 받아오고 요청을 하여 토큰 값을 불러올 수 있다. 그런데 get 요청전에 토큰 검증을 하고 허가가 되어야 html을 로드해야 하지 않을까? 그렇다면 서버에서 처리를 해야하는가? 배워가는 학부생으로 많이 부족하다고 생각합니다,, 선배님들의 견해를 받고 싶어 게시물 올립니다..! 핵심 코드는 다음과 같습니다.

1서버측 
2
3(server.js)
4app.get('/', loginRequired, (req, res) => {
5    res.sendFile(__dirname + '/views/index.html'); 
6});
7
8(login-required.js)
9const jwt = require('jsonwebtoken');
10
11const loginRequired = async (req, res, next) => {
12    // TEST CODE
13    const userToken = req.headers["authorization"]?.split(' ')[1]; // request 헤더로 부터 authorization bearer 토큰 받음 (공부 필요)
14    console.log(userToken + '             TEST===1');
15
16    // if(userToken) { <= 이 코드로 실행시 무한로딩
17    //     next();
18    // }
19    next() // undefined, 실제토큰 순으로 콘솔에 찍힘
20
21}
22
23module.exports = loginRequired;
24
25
26프론트측
27
28(test.js)
29const userToken = localStorage.getItem('token');
30if (userToken) {
31    const headers = {
32        authorization: `Bearer ${userToken}`,
33    };
34
35    axios.get('/', { headers, withCredentials: true })
36        .then(response => {
37            // console.log(response.data);
38        })
39        .catch(error => {
40            console.error(error);
41        });
42} else {
43    // 토큰 값이 없을때 구현해야 하는 로직
44}
이 질문이 도움이 되었나요?
'추천해요' 버튼을 누르면 좋은 질문이 더 많은 사람에게 노출될 수 있어요. '보충이 필요해요' 버튼을 누르면 질문자에게 질문 내용 보충을 요청하는 알림이 가요.
profile picture
익명님의 질문

답변 2

남 재민님의 프로필 사진

작동되는지 모르겠지만, 혹시 발상의 전환이 되지 않을까 싶어서 일단은 남겨볼게요. (server.js) app.get('/', loginRequired, (req, res) => { if res.status===200 res.sendFile(__dirname + '/views/index.html'); }); (login-required.js) const jwt = require('jsonwebtoken'); const loginRequired = async (req, res, next) => { try { 토큰취득, 디코딩 next(); } catch (err) { return res.status(401).json({ message: 'Not authenticated' }); } } - try catch문으로 토큰취득 실패시 인증실패 메세지 설정 근데 304 이라 콘솔에 cors에러나 다른에러 안뜨면 다행이네요

profile picture

익명

작성자

2024년 04월 06일

재민님 안녕하세요 ! 좋은 답변 감사합니다. 위 방법도 사용해보겠습니다. 보다 먼저 AI 봇이 달아준 답변이 있어 원인을 알게 되었고 시도를 하니 해결하였습니다. 저와 같은 문제가 생길 수 있는 분도 있으니 이 글을 남겨두려고 합니다. 감사합니다 !

남 재민님의 프로필 사진

ai봇 답변이 보이지 않아서 원인이 궁금한데 공유 부탁드려도 될까요?🙂

profile picture

익명

작성자

2024년 04월 07일

ai 답변이 5일뒤에 다른 분들께 공개가 된다고 하네요 ! 제가 복붙해드리면 답변은 다음과 같습니다 당신의 코드를 살펴보면 문제는 서버가 '/'에 대한 요청을 두 번 받고 그 사이에 클라이언트 측 JavaScript가 실행되어 localStorage에서 토큰을 가져오는 것 같습니다. 이로 인해 첫 번째 요청에서는 토큰이 정의되지 않았으며, 다음 요청에서만 토큰이 존재하는 것입니다. 일반적으로 JWT authentication의 경우, 로그인 api 호출 후 서버가 token을 반환하고 이 token을 client side에서 저장합니다. 로그인 이외의 모든 secure routes 호출 시 request header 안에 Authorization 헤더와 함께 이 token을 보냅니다. 따라서 여기서 문제점은, 최초 접속 시 '/' 라우터가 html 파일을 반환하게 되며, 이 때 axios를 활용한 추가 GET 요청이 발생하여 JWT token이 server로 전달되는 구조입니다. 하지만 배포나 실제 상황을 고려한다면 굳이 같은 엔드포인트('/')를 2번 호출할 필요는 없습니다. 클라이언트 코드를 수정하여 서버로부터 HTML 페이지를 처음 로드할때 보안 토큰 없이 리소스를 가져오도록 하세요. 그런 다음 사용자가 나중에 인증된 작업(예: 보호된 API 엔드포인트에 대한 요청)을 수행하려고 할 때 토큰을 사용하세요. 이 방법은 HTML의 초기 로드를 블록하지 않으므로 페이지가 더 빨리 로드됩니다. 다음과 같은 구조를 추천합니다: 1. 클라이언트는 로그인 API를 호출하고 서버로부터 token을 받아와 localStorage에 저장한다. 2. 이후 보안이 필요한 API 호출 시에는 헤더에 해당 token을 담아서 요청한다. 3. 서버에서는 해당 API 요청이 들어올 때 마다 Authorization 헤더에 있는 token을 확인하여 인증한다. 따라서 프론트쪽에서 '/' 경로를 fetch 하는 axios.get('/') 를 없애도 됩니다. ```javascript //test.js를 이렇게 바꿉니다: const userToken = localStorage.getItem('token'); if (userToken) { const headers = { authorization: `Bearer ${userToken}`, }; axios.get('/secure-endpoint', { headers, withCredentials: true }) .then(response => { // console.log(response.data); }) .catch(error => { console.error(error); }); } else { // 토큰 값이 없을때 구현해야 하는 로직 } ``` 서버 측의 코드를 수정하여 '/secure-endpoint' 경로에서 JWT 검증을 하게 해 주세요: ```javascript //server.js app.get('/secure-endpoint', loginRequired, (req, res) => { res.json({data: "secure data"}); }); ``` 이렇게 하면 클라이언트쪽에서 페이지 로드 후 인증이 필요한 API를 호출하게 됩니다. 이때 JWT 토큰을 함께 보내므로 서버에서는 이를 검증할 수 있습니다.

지금 가입하면 모든 질문의 답변을 볼 수 있어요!

현직자들의 명쾌한 답변을 얻을 수 있어요.

또는

이미 회원이신가요?

목록으로
키워드로 질문 모아보기

실무, 커리어 고민이 있다면

새로운 질문 올리기

지금 가입하면 모든 질문의 답변을 볼 수 있어요!