URL 인코딩은 웹에서 데이터를 안전하게 전송하기 위해 사용되는 퍼센트 인코딩 방식입니다. 이를 통해 URL에 사용할 수 없는 문자(공백, 특수 문자, 비아스키 문자 등)를 전송 가능한 형태로 변환하여 데이터의 손실이나 변조를 방지합니다. URL 인코딩은 URL의 구조를 유지하고, 서버 오류를 방지하는 역할을 합니다.
그 예시로 공백은 %20
으로 표현되고, +는 %2B
로 표현됩니다.
예: 공백( ) → %20, ! → %21
공백
이 +
로 변환되고 URL에서는 %20
이면 공백으로 변환됩니다.예: %20 → 공백( ), %21 → !
+
이 공백
으로 변환되고 URL에서는 %2B
이면 그대로 +
로 변환됩니다.?key1=value1&key2=value2&... (쿼리 문자열)
key = value (쿼리 파라미터)
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { AxiosError } from "axios";
import { basicAxios } from "@/api/axios";
const useOAuth2 = (provider: string) => {
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const startOAuth2Flow = () => {
const redirectUri = encodeURIComponent("https://localhost(도메인주소)/login");
let oauth2Url = "";
if (provider === "google") {
oauth2Url = `https://api.fordogs.store/oauth2/authorization/google?redirect_uri=${redirectUri}`;
} else if (provider === "kakao") {
oauth2Url = `https://api.fordogs.store/oauth/authorize/kakao?redirect_uri=${redirectUri}`;
}
if (oauth2Url) {
window.location.href = oauth2Url;
} else {
setError("지원하지 않는 OAuth 제공자입니다.");
}
};
useEffect(() => {
const urlParams = new URLSearchParams(window.location.search);
const rawAuthCode = urlParams.get("code");
const authCode = rawAuthCode ? rawAuthCode.replace(/ /g, "+") : null;
// const formattedAuthCode = authCode ? decodeURIComponent(authCode) : null; 불디코딩 부분 제거
const getJwtWithCode = async (code: string) => {
try {
setLoading(true);
console.log("Auth Code:", code);
const response = await basicAxios.post("/users/login-with-code", {
authCode,
});
if (response.status === 201) {
const { userId, accessToken, expirationTime } = response.data.result;
localStorage.setItem("accessToken", accessToken.value);
localStorage.setItem("userId", userId);
localStorage.setItem("expirationTime", expirationTime); localStorage.getItem("expirationTime")
);
navigate("/");
}
} catch (err) {
const error = err as AxiosError;
console.error("요청 오류:", error);
if (error.response) {
console.error("서버 응답 데이터:", error.response.data);
}
setError("로그인에 실패했습니다. 다시 시도해주세요.");
} finally {
setLoading(false);
}
};
if (authCode) {
getJwtWithCode(authCode);
}
}, [navigate, provider]);
return {
startOAuth2Flow,
loading,
error,
};
};
export default useOAuth2;
해당 코드는 OAuth2 기능을 구현할 때 URL 쿼리 파라미터 값의 인코딩과 디코딩 과정을 해결하여 api에 값을 전달해준 코드입니다.
window.location.search
를 통해 code = URL 쿼리 파라미터값을 가져와서 쿼리 파라미터를 파싱합니다.이 때 쿼리 파라미터 값을 파싱할 때 자동으로 디코딩 작업이 수행되어서 +
가 공백
으로 변하는 작업이 이루어집니다.
const authCode = rawAuthCode ? rawAuthCode.replace(/ /g, "+") : null;
: 만약 공백이 있으면 +로 변환합니다.
const formattedAuthCode = authCode ? decodeURIComponent(authCode) : null;
: authCode가 존재하면 decodeURIComponent를 사용하여 다시 디코딩됩니다. %xx
형식은 해당 기호로 변환해주고 +는 공백으로 변환해줍니다.(해당 코드는 URL 쿼리 파라미터 값을 가져올 때 자동으로 디코딩 되는 것을 공부하는 중간에 깨달아서 삭제했습니다.)
❓: 공백
을 +
로 replace 한 후 디코딩 과정에서 다시 공백이 안됐는지는 의문 -> Base64로 인코딩된 문자열 디코딩 했기 때문에 원래 데이터가 출력
결론은 플젝 친구에게 설명하기 위해서 시작한 공부와 URL 쿼리 파라미터의 자동 디코딩 문제로 인해 인코딩과 디코딩에 대해서 공부하면서 코드의 불필요한 부분을 찾게된 좋은 시간이였습니다.
후기. 서버에서 URL을 보낼 때 인코딩을 해서 보냈더라면 +가 공백으로 디코딩이 안되고 %2B로 오기 때문에 클라이언트에서는 +로 디코딩 되면 제대로 보내졌을거라는 토론이 오갔습니다.