Coding History/Team Project

팀플) 회원의 별자리 데이터 요청 로직

BlackBirdIT 2024. 11. 16. 00:50

이제 즐겨찾기 기능을 완성했으니까, 별자리 데이터 요청을 회원도 할 수 있게 됨. (자동으로 되는거긴 하지만)

그에 대한 하드코딩을 지우고 이제 제대로 백엔드와 통신후에 요청이 되도록 수정하면 될듯.

function StarMap() {
    const [constellationData, setConstellationData] = useState(null);
    const [error, setError] = useState(null);
    const { location, isLoading } = useUserLocation(); // 사용자 위치와 로딩 상태를 가져옴
    const { isAuthenticated } = useAuth(); // 로그인 상태 확인
    const { userId } = useParams();

    // 현재 날짜를 "YYYY-MM-DD" 형식으로 가져오는 함수
    const getTodayDate = () => {
        const today = new Date();
        const year = today.getFullYear();
        const month = String(today.getMonth() + 1).padStart(2, '0'); // 월은 0부터 시작하므로 1을 더해줌
        const day = String(today.getDate()).padStart(2, '0');
        return `${year}-${month}-${day}`;
    };

    useEffect(() => {
        if (isLoading) {
            // 로딩 중일 때는 요청을 보내지 않음
            return;
        }

        // 오늘 날짜 가져오기
        const today = getTodayDate();

        // 백엔드 API 요청 보내기
        const fetchData = async () => {
            try {
                let url = `/constellations`;
                if (isAuthenticated) {
                    url = `/constellations/${userId}`; // 로그인된 사용자일 경우, 유저 아이디를 포함한 경로로 요청
                }

                const currentLocation = isAuthenticated ? location : { latitude: 37.5665, longitude: 126.9780 }; // 로그인 상태에 따라 위치 설정
                const response = await axios.get(url, {
                    params: {
                        latitude: currentLocation.latitude,
                        longitude: currentLocation.longitude,
                        startDate: today, // 오늘 날짜로 설정
                        endDate: today, // 현재는 오늘 날짜만 사용 추후 수정
                    }
                });
                console.log('API Response:', response.data);
                setConstellationData(response.data);
            } catch (err) {
                setError(err);
                console.error('Error fetching constellation data:', err);
            }
        };

        fetchData().then(() => {
            console.log('데이터 요청 완료');
        });
    }, [location, isLoading, isAuthenticated, userId]);

삼항연산자로 로그인 하지 않은 유저에 대한 처리했고 유저 상세보기 페이지와 유사하게 유저상태 확인하는 로직까지 추가해서 구성함.

백엔드에서 혼선을 줄이기 위해서 /constellations/${userId}로 구분해서 요청할 수 있게 함,


그러고 이제 유저 별자리 백엔드 로직 처리중...

다 작성했고,

일단은 비회원의 기능이 망가지지 않았는지 확인

잘 됨.

하하 한번에 될리가 없지.


생각해보니까 userLocationService.js가 있다는걸 까먹었다. 내가 나중에 행성, 유성우 요청시에 이게 따로 분리되어있어야지 편할 것 같아서 분리 해뒀었는데 이거 존재를 까먹음 ㅋㅋㅋ 그래서 백엔드에서 따로 userId로 받던 로직 싹 지웠다. 저기서 애초에 위치를 설정해주고 요청하도록 하게끔.


하는김에 상세보기 페이지에

하나 만들어서 저장하고 다시 HistoryBack하는 로직을 짰다.
UI도 마찬가지로 toast로 구현했고 전 페이지로 리디렉션 잘 시킴.

여튼 다시 본론으로 가서

내가 DB를 초기화를 한번 했는데

요청을 보내니까 즐겨찾는 위치를 설정했음에도 불구하고 서울로 고정 요청을 보내는걸 확인함.

보니까 프론트에서 받은 유저 정보에서는 즐겨찾는 위치 값이 0으로 되어있었음. DTO가 업데이트 되지 않았다는 얘긴데, 이 문제를 해결해야됨.

어 가만생각해보니까 그게 아닌데,

사용자 상세 정보에서 처리를 하고 데이터를 가져오는 것도 DTO인데 여기서는 내가 위치 정보에 대한 설명을 저장해도 바로 적용됨.

근데 왜 별자리 데이터로 접근해서 들어온 데이터를 확인하면 여전히 즐겨찾기는 0으로 설정되어있지?


아 저건 DB에서 값을 가져와서 그런거다. 여튼 DB가 변동이 일어남이 감지되면 DTO가 업데이트 되게끔 하는 로직이 필요할듯하다.

아 힘들었다.

이걸 고치면서

Mapper를 도입함. mapstruct라는 의존성을 사용해서 자동으로 하게끔할 수 있대서 그것도 써봄.

import com.teamname.astroneer.star_info_web.dto.MemberDetailDTO;
import com.teamname.astroneer.star_info_web.entity.Member;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper(componentModel = "spring")
public interface MemberMapper {

    MemberMapper INSTANCE = Mappers.getMapper(MemberMapper.class);

    // Member -> MemberDetailDTO 매핑
    @Mapping(source = "id", target = "userId")
    MemberDetailDTO toMemberDetailDTO(Member member);

    // MemberDetailDTO -> Member 매핑 (필요 시)
    @Mapping(source = "userId", target = "id")
    Member toMember(MemberDetailDTO memberDetailDTO);
}

코드 보면 이런식으로.. 솔직히 말해서는 나도 뭐 어떻게 돌아가는건지 모름. 여튼 이렇게 해서..


문제의 원인

원인이 정확하게 뭐였냐면 로그인 유저의 인증 상태를 확인하는 메서드가 있는데 이게

    @GetMapping("/me")
    public ResponseEntity<MemberDetailDTO> getAuthenticatedUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (authentication != null && authentication.isAuthenticated() && !(authentication instanceof AnonymousAuthenticationToken)) {
            if (authentication instanceof OAuth2AuthenticationToken) {
                OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal();

                Optional<Member> memberOptional = memberService.findByEmail(Objects.requireNonNull(oAuth2User.getAttribute("email")));
                if (memberOptional.isPresent()) {
                    Member member = memberOptional.get();
                    MemberDetailDTO memberDetailDTO = memberMapper.toMemberDetailDTO(member);
                    return ResponseEntity.ok(memberDetailDTO);
                }
            }
        }
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
    }

이런 메서드임. 여기서 원래 정보를 가져왔는데 DB나 DTO 접근이 없게 만들어서 (지금은 수정된거임) 로그인 당시의 정보만 갖고있음.

그래서 내가 정보를 업데이트를해도 이 정보만 갖고 재활용하고 있어서 뭐가 안됐던거임..

여튼 이걸 인증을 확인하면서 정보도 갱신하게끔 만들어서 인증이 필요한 곳에 접근하면 알아서 갱신되게 로직을 짜봄. 그 와중에 Mapper 등을 도입하게 된 것.


여튼 그래서 결과는

현재는 4번으로 설정되어있는데,

즐겨찾기를 업데이트 하고, 다른 인증이 필요한 페이지로 접속을 시도하면

이렇게 갱신됨..

어휴 진짜 힘들다.


이제 다시 별자리 로직으로 넘어가서..

로직은 StarMap으로 접근시에 useUserLocation이 작동하고, 거기서 로그인인지 아닌지 체크 후에 로그인이면 getUserLocationFromProfile에서 즐겨찾기 아이디를 가져와서 useUserLocation위치를 셋업한다음 요청 보내고 StarMap으로 반환함.

왜이렇게 복잡하게 짰냐면, 나중에 행성이나 유성우에서도 같은 로직을 써야되니까 처음부터 중복코드 없애려고 이렇게 해둠..

여튼 결과는

DB랑 결과 비교 해보면,

됐다..


그럼 이제 즐겨찾기가 없는 유저, 로그인 하지 않은 유저에게 로그인 권유하는 toastUI만 넣으면 될듯?

useUserLocation.js를 손봤고 또또또 두번 요청되는 문제를 해결하고 난 이후 결과다. 로그인 했고, 즐겨찾는 위치가 있는 유저의 경우 요청이 한번만 되는 것도 확인했고, 이후에 로그인은 했지만 즐겨찾는 위치가 설정되지 않은 유저는 이런 화면을 볼 수 있다.

그리고 메세지를 클릭하면.

상세정보에서 위치 정보를 저장하러 갈 수 있게 했다.

비회원의 경우에는

이걸 클릭하면,

로그인 화면이다.


여담으로

이런 문제 때문에 toastUI가 두개가 동시에 출력되는 어지러운 상황이 있긴했는데

if (isAuthLoading) {
   return;
}

이걸로 처리해서 해결함.

좀 더 정확하게는 AuthProvider.js인증 관련로직이 있는 여기서 isAuthLoading로직을 추가하고,

    return (
        <AuthContext.Provider value={{ isAuthenticated, user, isAuthLoading, login, logout }}>
            {children}
        </AuthContext.Provider>
    );

이 정보까지 활용해서 인증상태가 확인될 때 까지 로딩하게 짰음.

아무튼 일단 이까지 했으면 됐다.