미새문지

24.09.03 day59 작업 일지 본문

개발 TIL

24.09.03 day59 작업 일지

문미새 2024. 9. 3. 22:38
728x90

타입 맞추는게 너무 거지같다. 로그인에서 계속 에러가 발생하는데 해결이 안되서 계속 붙잡다가 일단락은 됐다.

import React, { useState } from "react";
import "../../scss/login/emailLogin.scss";
import CommonInput from "../common/commonInput";
import PwVisible from "../common/pwVisible";
import axios from "axios";
import jwt_decode, { JwtPayload } from "jwt-decode";

interface MyTokenPayload extends JwtPayload {
  exp: number;
  iat?: number;
  userId?: string;
  email?: string;
}

const EmailLoginBox: React.FC = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [showPassword, setShowPassword] = useState(false);

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    try {
      const userData = {
        email,
        password,
      };

      console.log(userData);

      const response = await axios.post("/api/user/login", userData);
      console.log("로그인 성공", response.data);

      const token = response.data.token;
	const decodedToken = jwt_decode<MyTokenPayload>(token);
      const expirationTime = decodedToken.exp * 1000;

      sessionStorage.setItem("authToken", token);
      sessionStorage.setItem("tokenExpiration", expirationTime.toString());

      axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;

      window.location.href = "/";
    } catch (error) {
      console.log("로그인 실패", error);
    }
  };

  return (
    <>
      <div className="loginInputForm">
        <form onSubmit={handleSubmit}>
          <CommonInput
            typeValue="email"
            placeholderValue="이메일"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
          />
          <CommonInput
            typeValue={showPassword ? "text" : "password"}
            placeholderValue="비밀번호"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
          />
          <PwVisible onToggle={setShowPassword} />
          <span className="forgotPw">
            비밀번호를 잊으셨나요? <span>비밀번호 재설정</span>
          </span>
          <button className="loginBtn" type="submit">
            로그인
          </button>
        </form>
      </div>
    </>
  );
};
export default EmailLoginBox;

서버에 요청을 위해 api로 이메일과 비밀번호 정보를 보내는데 요청에 성공했을 시 서버에서는 토큰을 보내줄 거라 해당 토큰을 저장하는 방식을 작성했다. 세션스토리지에 토큰을 저장하는데 타입스크립트에서도 jwt적용할 수 있다고 해서 기본적인 세팅을 찾아서 작성했는데,

const decodedToken = jwt_decode<MyTokenPayload>(token);

이 코드에서 이 식은 호출할 수 없습니다.
'typeof import("c:/Users/user/Desktop/cosmetic_routine/corou-frontend/node_modules/jwt-decode/build/cjs/index")' 형식에 호출 시그니처가 없습니다.
ts(2349)

라는 에러메세지가 떴다.

jwt를 사용하기 위해선 jwt-decode라는 라이브러리를 설치해줘야 하는데, 설치 후 jwt_decode로 토큰을 넣어주면 된다고 하더라

근데 위 에러가 계속 떠서 해결법을 찾아봤더니 타입을 호출할 수 없다고 모듈 설치를 제대로 했는지 확인하래서 라이브러리 설치와 해당 라이브러리 타입을 지정해주는 모듈도 설치했고

npm install jwt-decode
npm install @types/jwt-decode --save-dev

설정에서 타입을 못받아올수도 있다고 해서 tsconfig도 설정을 해주었다.

{
  "compilerOptions": {
    "typeRoots": ["node_modules/@types"], // 타입 경로를 지정해서 가져올 수 있게
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": ["src"]
}

그래도 타입을 못읽는 건지 여전히 인식이 안되서 지피티를 사용한 결과 타입을 강제로 지정해주면 된다해서 decodedToken의 타입을 강제로 지정해줬다.

const decodedToken = (jwt_decode as unknown as (token: string) => MyTokenPayload)(token);

토큰의 만료시간을 설정해줬는데 이건 서버에서 어떻게 들어오냐에 따라 다르기 때문에 작성만 해두고 필요없으면 이후에 지우면 될 것 같다.

그리고 token관련해서 지피티 좀 물어보면서 이것저것 찾아봤는데

axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;

이걸 작성하면 axios의 헤더에 Bearer token을 기본설정으로 잡아준다고 해서 작성해놨다.

로그인에 성공 시 메인페이지로 이동되고 실패하면 실패 메세지와 함께 error코드가 출력된다.

 

데이터를 요청하는 handleSubmit의 경우 처음에 로그인 버튼에 onClick으로 지정해놨는데 이건  form의 onSubmit에 작성해주는게 더 자연스럽다고 하여 코드를 수정해줬다.

 

로그인은 api연결할 때 더 확인해보고 마이페이지의 프로필 부분은 useEffect를 이용해 렌더링될 때 정보를 가져오게 작성했다.

get방식으로 /api/user/self로 데이터를 요청했다. 

import { useEffect, useState } from "react";
import "../../scss/mypage/profile.scss";
import CommonTag from "../common/commonTag";
import axios from "axios";

interface userProfile {
  nickname: string;
  profileImg: string;
  trouble: string[];
}

const Profile: React.FC = () => {
  const [profile, setProfile] = useState<userProfile | null>(null);

  useEffect(() => {
    axios
      .get("/api/user/self")
      .then((response) => {
        setProfile(response.data);
      })
      .catch((error) => {
        console.error("프로필 조회 실패", error);
        setProfile(null);
      });
  }, []);

  return (
    <>
      <div className="profileWrapper">
        <div className="profileImg">
          <div>
            <img
              src={profile?.profileImg}
              alt={`${profile?.nickname} 프로필`}
            />
          </div>
          <p>{profile?.nickname}</p>
        </div>
        <div className="selectFilter">
          {/* <CommonTag tagName="건성" />
          <CommonTag tagName="남성" />
          <CommonTag tagName="30대" />
          <CommonTag tagName="민감성" />
          <CommonTag tagName="겨울쿨" />
          <CommonTag tagName="등" />
          <CommonTag tagName="등등" /> */}
          {profile?.trouble.map((item, index) => (
            <CommonTag key={index} tagName={item} />
          ))}
        </div>
        <button>프로필 수정</button>
      </div>
    </>
  );
};
export default Profile;

이것도 서버에서 데이터가 어떻게 들어오냐에 따라 수정해야 하지만 기본적으로 이렇게 들어올 것 같고 기획중에 프로필 사진에 대한 기획을 깜빡해버려서 이건 나중에 회의로 결정해야 할 것 같다. 중요한 부분은 아니기 때문에 이 후 추가 기능으로 구현하려고 한다. 내일은 카카오 소셜 로그인을 구현해보고 재희님이 작성해준 api 명세서를 보면서 데이터 조회부분을 하나씩 적용시킬 예정이다.

 

728x90

'개발 TIL' 카테고리의 다른 글

24.09.05 day61 작업 일지  (0) 2024.09.05
24.09.04 day60 jwt 오류 해결, 카카오 로그인 구현 중  (3) 2024.09.04
24.09.02 day58 작업 일지  (4) 2024.09.02
24.08.30 day57 작업 일지  (0) 2024.08.30
24.08.29 day56 작업 일지  (0) 2024.08.29