발단

const axios = require('axios');
const jwt = require('jsonwebtoken');

// 비밀키를 비동기적으로 얻기 위한 함수 (외부 API 호출)
function getSecretKey(callback) {
  axios.get('<https://example.com/getSecretKey>')
    .then(response => {
      const secretKey = response.data.secretKey;
      callback(null, secretKey); // 에러는 null, 비밀키는 응답 데이터에서 가져온 값
    })
    .catch(error => {
      callback(error, null); // 에러 발생 시 에러 객체를 전달
    });
}

// JWT 생성을 위한 콜백 함수
function generateJwt(payload) {
  return function (err, secret) {
    if (err) {
      console.error('Failed to get secret key:', err);
      return;
    }

    jwt.sign(payload, secret, (err, token) => {
      if (err) {
        console.error('JWT signing error:', err);
        return;
      }

      console.log('Generated JWT:', token);
    });
  };
}

// 비밀키를 얻고 JWT 생성
const payload = { user_id: 123456 };
const jwtCallback = generateJwt(payload); // 클로저를 활용하여 콜백 함수 생성

getSecretKey(jwtCallback);

위의 코드를 이해하기가 힘들었다.

 

return function은 말 그대로 함수를 다시 반환하는 것일텐데.. 뭔가 어떻게 함수를 반환한다는 것인지 이해가 안 갔다.

문제

먼저, 이 코드를 이해하려면 일급 객체와 해당 문서 안에 설명되어 있는 고차함수, 콜백 정도는 제대로 알고 있어야한다고 생각한다.

자바스크립트의 1급 객체의 특성을 이용해서, 함수를 리턴하는 예시를 보여보자면 이런게 있겠다.

function add(num1) {
	return function (num2) {
    return num1 + num2;
	};
}

add(1)(2) // = 3

인자 num1을 그대로 저장해서 리턴된 함수에서 써먹을 수 있는 모습을 볼 수 있다.

 

극단적으로 이런 것도 당연히 가능하다. (다른 파일이라는 가정 하에)

function add(num1) {
  return function (num2) {
    return function (num3) {
      return function (num4) {
        return num1 + num2 + num3 + num4;
      };
    };
  };
}

add(1)(2)(3)(4); // = 10

결론

내가 읽지 못하던 코드는 막 어려운 내용이 아니었다.

내가 return function을 잘 이해하지 못해서 생긴 문제였다. 자바스크립트에 대한 이해도가 낮았다.

 

이해가 가는 입장에서 데이터의 흐름을 번호로 달아서 설명을 해보자면,

const axios = require('axios');
const jwt = require('jsonwebtoken');

function getSecretKey(callback) {
  axios.get('<https://example.com/getSecretKey>')
    .then(response => {
      const secretKey = response.data.secretKey;
      callback(null, secretKey);
    })
    .catch(error => {
      callback(error, null);
    });
}

function generateJwt(payload) {
  return function (err, secret) {
    if (err) {
      console.error('Failed to get secret key:', err);
      return;
    }

    jwt.sign(payload, secret, (err, token) => {
      if (err) {
        console.error('JWT signing error:', err);
        return;
      }

      console.log('Generated JWT:', token);
    });
  };
}

const payload = { user_id: 123456 };
const jwtCallback = generateJwt(payload); // 1. generateJwt에 의해 함수가 생성된다. 이 함수는 jwtCallback에 대입된다.

getSecretKey(jwtCallback); // 2. getSecretKey에 jwtCallback에 대입된 함수가 인자가 들어간다.

// 3. getSecretKey가 작동하면서 'example.com/getSecretKey' 에서 정보를 받아온다.

// 4-1. 정보를 받아오는데에 성공했다면, .then 으로 넘어가서 jwtCallback에 대입되었던 함수로 넘어간다. 첫번째 매개변수인 err이 null 값으로, 토큰 값을 출력한다.
// 4-2. 정보를 받아오는데에 실패했다면, .catch 로 넘어가서 jwtCallback에 대입되었던 함수로 넘어간다. 첫번째 매개변수인 err에 값이 있다. 따라서 에러를 출력하고 리턴된다.

이 정도로 설명할 수 있겠다.

코드가 이해가지 않을 땐 나눠서 이해해보려고 하면 금방 이해가 가는듯 하다.

 

잘못된 부분이 있다면 댓글 부탁드립니다!!

발단

jsonwebtoken을 사용하다보면 자연스레 jwt.sign이라는 함수를 접하게 된다. 

jwt.sign(payload, secretOrPrivateKey, [options, callback])

jwt.sign()은 이런 방식으로 사용할 수 있는데, callback 부분이 모호했다.
이 함수의 콜백에서는 첫 번째 인자로 토큰 생성 실패시 에러, 두 번째 인자로는 생성 완료시 만들어진 토큰이 들어온다. 근데 콜백을 굳이 쓸 이유가 없어보였다.

문제

function sign(id, email, secret) {
  return new Promise((resolve, reject) => {
    jwt.sign({ accountID: id, email: email }, secret, (error, token) => {
      if (error) {
        reject(error);
      } else {
        resolve(token);
      }
    });
  });
}

jwt.sign 함수를 이렇게 사용하는 꼴을 많이 봤다. 
jsonwebtoken 깃헙도 찾아보았으나, 콜백 사용에 대한 뚜렷한 이유가 없어보였다.
기본적으로 프로미스, 콜백이 섞이다보면 가독성이 떨어진다고 느끼는데(내가 많이 초보여서 그럴 수도 있다.),
전부 async await과 같은 패턴으로 만드는 것이 분명히 가능해보였다.

let signedToken

jwt.sign(payLoad, 'secret', (err, token) => {
  if (err) throw err
  console.log(token) // Token logs here successfully
  signedToken = token
  console.log(signedToken) // signedToken logs successfully
})

console.log(signedToken) // logs 'Undefined'

이런 코드에선 오히려 jwt.sign이 완료되기 전에 마지막 라인이 실행되어 Undefined가 출력되는 것도 확인할 수 있었다. (당연하긴 하다.)
어떤 상황에서 콜백을 사용해야 하나 궁금했다.

결론

https://stackoverflow.com/questions/54629127/is-there-a-benefit-to-performing-jwt-operations-as-async

 

Is there a benefit to performing JWT operations as async?

I'm using the highly popular jsonwebtoken package for node. Signing and verifying are typically performed synchronously, but async options are also provided. I thought that the operations involved...

stackoverflow.com

내가 사용하는 환경에선 의미가 없었다.
 
jwt.sign, jwt.vertify의 2번째 인자로 들어가는 비밀키를 비동기적으로 얻을 때. 그 때 만큼은 필요한듯 했다.
키를 얻는데에 시간이 오래 걸리면 다른 작업이라도 먼저 해야하니까 콜백을 사용해서 비동기적으로 처리할 수 있다는데...... 어차피 sign, vertify 이후의 일은 마지막에 인증된 사용자에 대해서 해줄만한 일 아닌가? 
- sign 이후엔 쿠키에 담아서 보내주기, 계정 DB에 저장하기 정도..
- vertify 이후엔 decode된 값과 대조하기 정도..
내 수준에선 이 정도밖에 떠오르지 않았고, 확실한 것은 현재 내 상황에선 의미가 없다는 것이었다.
 
위의 스택오버플로우 글을 보면 crypto 메소드 또한 동기적이라고 하니, 사용법이나 잘 알아두면 되겠다는 생각이 든다.
 
잘못된 부분이 있다면 댓글 부탁드립니다!!

발단

카카오테크캠퍼스 울 팀원 분의 과제를 도와주는데, 뭔가 이상한 점이 있었다.

useQuery에 사용하는 api 호출 함수에서 async await을 사용하지 않는 것이었다. 그리고 정상적으로 작동하고 있었다.

문제

// 1번
export const apiCall = (method, path, data) => {
    return axios({method, url: API_PREFIX + path, data})
        .then(resp => resp.data)
};

// 2번
export const apiCall = async (method, path, data) => {
    const response = await axios({method, url: API_PREFIX + path, data})
    return response.data
};

제가 여태 알던 것은 위의 두 가지와 같은 형태가 리액트 쿼리의 함수로 들어가야한다고 알고 있었는데, 밑의 코드 또한 리액트 쿼리에 의해 자동으로 정상작동 한다.. 라고 이해하면 맞을까요??

export const apiCall = (method, path, data) => {
    const response = axios({method, url: API_PREFIX + path, data})
    return response
};

위의 내용은 내가 2단계 질문 방에 올린 내용이었다.

나는 axios의 요청을 기다리다가 response에 undefined 값이 들어와서 그대로 리턴될 줄 알았다.

내 생각엔 비동기적으로 처리하는 과정에서 axios 처리는 따로 하고, 그렇다면 response는 아직 undefined일 테고, 정상적인 값이 반환되면 안 되지 않나?
리액트 쿼리가 undefined를 처리하는게 가능한가? 싶었다.
https://stackoverflow.com/questions/70388520/why-are-async-api-calls-necessary-with-react-query

 

Why are async api calls necessary with react-query?

Every piece of documentation shows async calls being used with react-query, but I am a little confused why these are necessary, as the following code works with or without async/await: export const

stackoverflow.com

스택오버플로우에서 비슷한 글을 찾았으나, 역시 나처럼 사용하고 있었다. 나는 내가 맞는 줄 알았다.

해결

axios에 의해 리턴되는 값은 Pending 상태의 Promise 였다.

async await을 통해 비동기 처리를 해주면 해당 값이 제대로 출력되며, 비동기 처리를 해주지 않아도 Promise를 반환하는 것이었다!!

그리고 Pending 상태의 Promise를 React Query에서 처리해주는 것이었다.

 

axios가 프로미스를 리턴한다는 것은 어떻게 보면 매우 당연한 것이었는데, 프로미스에 대한 이해가 많이 떨어졌던 것 같다.

이젠 완벽 이해했다. return new Promise((resolve, reject) => {...})의 형태도 왜 이렇게 생긴 건지 이해해버렸다....... 매우 값진 하루이다.

발단

내가 만든 JWT 토큰은 카테캠에서 실습할 때와 다르게 Bearer 키워드가 앞에 없었다. 나도 Bearer 토큰을 만들고 싶었다.

문제

chatGPT에게 내가 현재 JWT로 토큰을 생성하고 있는데, Bearer 키워드가 붙은 토큰으로 만들어달라고 했다.
근데 이런 답변을 받았다.
’Bearer ‘ 문자열을 앞에 덧붙이라는 응답이었다.

예, Bearer 쿠키를 반환하려면 Express에서 res.cookie() 메서드를 사용하여 아래와 같이 구현할 수 있습니다.

javascriptCopy code
const express = require('express');
const app = express();

app.get('/set-cookie', (req, res) => {
  const token = 'your_generated_token';
  res.cookie('access_token', `Bearer ${token}`, { httpOnly: true, maxAge: 1000 * 60 * 60 * 24 * 7 });
  res.send('쿠키가 성공적으로 설정되었습니다!');
});

app.listen(3000, () => {
  console.log('서버가 <http://localhost:3000> 에서 실행 중입니다.');
});

이 예제에서는 클라이언트가 /set-cookie 엔드포인트로 요청을 보내면 서버가 access_token 쿠키를 설정하고, Bearer 스키마를 포함하여 해당 토큰 값을 반환합니다. 쿠키는 maxAge 옵션에 지정된 기간 동안 유효하며, httpOnly 옵션으로 인해 JavaScript를 통해 접근할 수 없으므로 특정 유형의 공격으로부터 보다 안전합니다.

결론

Bearer 키워드는 그냥 상호간의 약속이었다.
따로 Bearer JWT를 만드는 방법이 있다던가, 하는 것은 없었다.
다른 타입의 Authorization과 비교, 식별하기 위해서 사용하는 키워드였다.

추가적으로 Bearer는 '옮기는 사람' 과 같은 의미를 갖고 있다고 한다.

다른 키워드는 이 링크에 더 있다.

스택 오버플로우 글은 여기에 있다.

+ Recent posts