Coding History/Team Project

팀플) 오늘 한 것.. (JWT 완전 도입 완료, 세세한 수정, 상세 페이지 CRUD)

BlackBirdIT 2024. 12. 3. 12:53

일단 develop으로 머지 시키고 개꿀잠을 자버림. (학원 못감, 사실 너무 늦게 잔 것도 있고 무조건 해결하고 자야겠다 싶어서 감기기운도 좀 있고 머리도 아픈데 끝까지 붙들고 JWT 모든 문제 해결 이후, 기존 로직들이 유지되게끔 수정하고 잠.)

근데 이제 팀원들이 머지하고 난 이후 서버가 돌아가지 않는다고 연락이 옴.


디스코드로 한 4시간 붙들고 얘기를 했는데, 나는 아무문제 없이 잘 작동하는데 팀원들만 안되는 기이한 상황이였음.

여기 보면 당시 어지러운 상황이 좀 남아있긴한데,,

결론은 윈도우랑 맥의 차이기도 했고,,

여튼 JWT인증을 위해서 나는 뭐 영어로 우리팀 개짱짱맨이고 아무도 막을 수 없으셈 이런걸 영어로 작성해서 Base64인코딩한 값을 넣었었음. 이 과정에서 맨 마지막에 =이라는 문구가 하나 더 붙게 됨.

그니까 이게 이제 문제의 시발점이였음.

Base64인코딩을 하면 4의 배수 어쩌구 하는 식으로 되는데 여기서 부족한 자리 값을 마지막에 = 또는 ==으로 채워서 4의 배수로 만든다고 함.

내가 만들었던 문구가 여기 걸리게 되어서 마지막에 =이라는 문구가 더 붙게 되어서 .env에 저장된 값을 윈도우는 KEY=VAULE=으로 읽게되며 그래서 "KEY가 뭐임?" 이러는 상황을 겪게된 것.

그래서 base64디코딩도 가능함과 동시에 뒤에 =이 붙지 않는 랜덤한 값을 인코딩해서 팀원들에게 나눠줬더니 말끔이 해결 됐음.


그리고 난 지금 새벽 2시 55분이지만 오늘 밤을 새고 학원에 갈듯함.

잤으니 할거 해야겠지?

지금 짜잘한 것들을 고치려고 하는데 우선

날씨 검색도 할 수 있게 만들었고, 기존 새로고침시에 다시 서울로 요청하던 것을 로직에 대한 조건을 더 빡빡하게 걸어줘서 해결함.

// useUserLocation.js

import { useEffect, useRef, useState } from 'react';
import { useAuth } from "../services/AuthProvider";
import { getUserLocationFromProfile } from "../services/userLocationService";
import { useNavigate } from 'react-router-dom';

function useUserLocation() {
    const [location, setLocation] = useState(() => {
        // 컴포넌트 마운트 시 Local Storage에서 위치 데이터 로드
        const savedLocation = localStorage.getItem('userLocation');
        return savedLocation ? JSON.parse(savedLocation) : null; // 초기값을 Local Storage에서 가져옴
    });
    const [isLoading, setIsLoading] = useState(true);
    const { isAuthenticated, user, isAuthLoading } = useAuth();
    const isMountedRef = useRef(true);
    const navigate = useNavigate();

    // 기본 위치를 사용할 때 도시 이름도 설정하도록 추가
    const [cityName, setCityName] = useState(() => {
        // 초기 값이 없는 경우 Unknown City로 설정
        const savedLocation = localStorage.getItem('userLocation');
        return savedLocation ? JSON.parse(savedLocation).cityName || 'Unknown City' : 'Unknown City';
    });

    useEffect(() => {
        const fetchUserLocation = async () => {
            if (isAuthLoading) {
                console.log("useUserLocation - 인증 로딩 중입니다. 로딩 대기...");
                return;
            }

            try {
                console.log('useUserLocation - 로그인 상태 확인:', isAuthenticated, '사용자 정보:', user);

                // 인증된 사용자인 경우
                if (isAuthenticated && user?.userId) {
                    console.log('useUserLocation - 로그인된 사용자입니다. 사용자 프로필에서 위치 정보 가져오기...');
                    console.log(`userId: ${user.userId}`);
                    const userLocation = await getUserLocationFromProfile(user.userId);

                    if (userLocation) {
                        // 현재 location과 가져온 userLocation이 다른 경우에만 업데이트
                        if (!location || location.latitude !== userLocation.latitude || location.longitude !== userLocation.longitude) {
                            setLocation(userLocation);
                            setCityName(userLocation.cityName || 'Unknown City'); // 사용자 위치의 도시 이름 설정
                            localStorage.setItem('userLocation', JSON.stringify(userLocation)); // 사용자 위치를 Local Storage에 저장
                            console.log('useUserLocation - 사용자 저장 위치로 요청 성공:', userLocation);
                        } else {
                            console.log('useUserLocation - 사용자 위치가 이미 설정되어 있습니다.');
                        }
                    } else {
                        console.log('useUserLocation - 사용자 위치 정보가 없습니다.');
                    }
                }

                // 인증되지 않은 경우, Local Storage에서 위치 데이터 제거
                if (!isAuthenticated) {
                    console.log('useUserLocation - 로그인하지 않음. 기본 위치(서울) 사용 및 저장된 위치 제거');
                    localStorage.removeItem('userLocation'); // Local Storage에서 사용자 위치 제거
                    const defaultLocation = { latitude: 37.5665, longitude: 126.9780, cityName: "Seoul" };
                    setLocation(defaultLocation);
                    setCityName("Seoul"); // 기본 위치로 설정할 때 도시 이름 설정
                }

            } catch (error) {
                console.error('useUserLocation - 사용자 위치 가져오기 오류:', error);
            } finally {
                if (isMountedRef.current) {
                    setIsLoading(false);
                    console.log('useUserLocation - 로딩 상태 완료');
                }
            }
        };

        fetchUserLocation();

        return () => {
            isMountedRef.current = false;
            console.log('useUserLocation - 컴포넌트가 언마운트되었습니다.');
        };
    }, [isAuthenticated, user?.userId, isAuthLoading]); // 의존성 배열 명확히 설정

    return { location, cityName, isLoading };
}

export default useUserLocation;

이렇게.

이건 재사용을 우리 프로젝트에서는 꽤 많이 하게 될 코드라 빡빡하게 또 꼼꼼하게 설계했음.

여튼 내가 맡은 페이지의 스타일은 프론트가 할 예정이였지만 애들이 힘들어보여서 내가 좀 고침.

디테일적인 것도 고치고 디자인도 했음.

검색기능도 넣음.


그리고 이제 할 것은 미뤄두었던 Detail 페이지의 기능들임.

지금은 약간 개발에 필요한 필수 기능만 좀 넣어뒀는데, 수정 삭제 기능도 넣어야됨.

이게 지금 모습이고..

수정 삭제기능은 닉네임, 그리고 선호 관측시간은 아무래도 쓸모 없을 것 같으니 여기서 출력 안되게 만들어버리고.

나머지는 즐겨찾기 상태를 뭐 하트나 별 같은걸로 직관성 있게 바꿔주기?

그리고 저장된 위치의 삭제나 수정으로 가면 될듯.

그럼 하고 오겠음.



삭제는 쉽게 만들었는데 수정은 좀 힘들었음.

결론은 잘 만들었음.

여기서 1번을 고친다고 가정하고 움직여보면, 수정을 누르면 이제 map 페이지로 리디렉션함.

그럼 이제 기존 저장 로직에서 (수정모드)를 만들어서 기존로직에 약간 덮어쓰는 식으로 만들었음.

저장해둔 위치에 마커를 찍어주고 저장했던 설명이 적혀있음. 버튼도 위치 수정으로 바뀌어있고,

만약 내가 여기로 저장한다고 하면,

안내 메세지랑 전 페이지로 리디렉션함.

여기도 바로 적용되어있고.

이렇게 수정 기능도 마무리.


하다가 리프레시 토큰이 좀 제대로 동작하지 않는 것 같아서 토큰 만료시간 2분으로 주고 30초 남았을 때 리프레시 토큰 발급 받도록 바꿔서 테스트 좀 해봄.

아 내가 컨트롤러에 /refresh로 구현을 해뒀는데 이것도 시큐리티 필터체인으로 구현했어야했네,

고쳐보자.

결론부터 말하면 제대로 동작하게 만들었음.

엑세스 토큰의 갱신 시간을 50초로 하고 30초 이하일 때 재요청하게끔 설계해서 테스트 해봤고 지금 계속 정상작동해서 재요청후 발급받은 토큰을 사용해서 로그아웃 처리가 안됨.

JwtAuthenticationFilter에 리프레시 토큰에 대한 메서드도 두개 생성함.

authenticateUser, handleRefreshToken으로 관리하고 첫번째 메서드는 검증 담당, 두번째 메서드는 재발급 담당임.

그리고 만들어 뒀다던 /refresh에서 리프레시 토큰 Redis 접근후에 값 검증후 새로운 토큰 반환하게끔 로직을 짬.

그래서 뭐..

JWT도 완전 도입 완료다.

여튼 이제 머지만 하면 될듯.

앞으로,,,

팀원들은 디자인 디테일한 수정에 들어갈거고 나는 자동 배포 준비할거임.