Coding History/Team Project

팀플) Google Maps API 백엔드 통신 및 react-toastify 로 자연스러운 UI/UX 구현

BlackBirdIT 2024. 11. 8. 19:14

이제 대충 구색은 맞춰놨으니까, 백엔드로 위도 경도를 전송할 수 있게 하면 된다.

근데 그전에 시큐리티 설정부터 만져야될듯?

그리고 또 그전에는 확인 버튼이랑 저장 버튼도 만들었다.

<button onClick={handleSubmit} className="submit-button mt-3">검색</button>
<button onClick={saveHandleSubmit} className="submit-button mt-3">내 위치로 저장</button>

대충 일케 만들어두고 일단 함수는 깡통이다.

그럼 이제 데이터가 어떤 방식으로 전송되는지 부터 확인해야하니까.

아무대나 찍고 버튼 클릭하니까 lat, lng이 출력된다. 이제 이걸 갖다가 백엔드랑 엮어주면 됨.

우선 지금 로직을 짜기 위해서 Location에 대한 컨트롤러 서비스를 작성하고 있는데, 가만 생각해보니까 관측 위치를 하나만 저장할 수 있다. 왜? 유저 테이블에 위경도가 있으니까. 근데 그렇게 하기보다는 Location 테이블을 나눠놓는편이 수월할 것 같다는 생각이 들어서. 일단 그렇게 진행해보는게 좋을듯

그래서 user테이블에 favorite_location_id 필드 추가하고, 위도 경도 관련된 것은 삭제 했다.

그렇게 바꿔서 대충은 다 작성했고, 저장에 대한 로직은 로그인 한 사용자만 사용할 수 있기 때문에 시큐리티 설정도 조금 손봐야함.


여튼 일단은 로그인 하지 않은 사용자가 저장을 시도할 시에 어떻게 처리를할까? 부터 해결해야될 것 같았다.

근데 이미 내가 만들어뒀던, CustomAuthenticationEntryPoint가 있어서 이걸 조금 수정해줬다.

@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType("application/json");
        response.getWriter().write("{\"error\": \"Unauthorized\", \"message\": \"로그인이 필요합니다.\"}");
    }
}

이걸 이제 프론트로 전달해서 alert로 처리할 수 있게 설정을 해둔거고.

이제 로그인 하지 않고 시도시에 로그인이 필요합니다 라는 alert도 잘 뜬다.

근데 리디렉션을 안하네..

다시 수정후에는

확인 누르면,

ㅇㅇㅇㅇ 리디렉션 함.

    const saveHandleSubmit = async () => {
        if (marker) {
            console.log('Sending location to server:', marker);
            try {
                const response = await sendLocationToServer(marker);
                console.log('Location saved:', response.data); // 성공적으로 저장된 후의 처리
            } catch (error) {
                if (error instanceof Error && error.message === 'Unauthorized') {
                    alert('로그인이 필요합니다.'); // 로그인 필요 에러 처리
                    navigate('/react/login');  // 로그인 페이지로 리디렉션
                } else {
                    alert('위치 정보를 서버로 보내는데 실패했습니다.'); // 기타 에러 처리
                }
            }
        } else {
            alert('관측 위치를 선택해주세요.');
        }
    };

이렇게 처리함.

이제 로그인 해보고 해보면

그치 당연히 안되지 백엔드 로직은 아직 다 안했음..

실패 처리 잘 되나 확인해봤다.


다른 얘기긴 한데 로그인 상태일 때, 로그인 페이지에 접근 되는 문제 발견함.


로그인 상태에서만 로그인 페이지의 애니메이션이 작동 되는 문제도 발견함..

일단은 로그인 페이지에서 애니메이션 제대로 작동 되는걸 확인하고 로그인 페이지 접근을 막자.

(이게 내가 만든게 아니라서 몰랐다.)


여튼 해결을 일단 했는데, 뭐가 문제냐면 애니메이션을 위해서 사용했던 이미지 파일 /particle.png가 있는데 이게 시큐리티에서 허용되지 않아서

이미지 보면 저게 인증받지 않은 상태라고 나와서 시큐리티 설정에 추가해줌..

시큐리티 진짜 개싫타.


다시 원래 문제 로그인 했는데 로그인 페이지에 접근 가능한 문제는 걍 시큐리티 말고 리액트로 처리하자.

엄 이게 좀 복잡한데, 인증 관리를 리액트에서도 좀 구현을 해줘야하고, 백엔드. 그니까 스프링에서도 같이 해서 서로 통신할 수 있게 로직을 잘 짜줘야한다.

확인 누르면 main으로 잘 돌아간다. 근데 로그인 페이지 까지 접속이 되는건 조금 이상한 것 같으니까 약간 바꿔야될듯

UI를 조금 더 유연하게 만들고 싶어서 뭐 좀 찾아봤는데, npm install react-toastify 토스트 메세지를 사용하면 뭔가 더 할 수 있을 것 같아서 받아서 써봐야겠다.

결과임.

import React, {useEffect} from "react";
import GoogleLoginButton from "./GoogleLoginButton";
import AstroAnimation from '../pixi/AstroAnimation.js'; // 애니메이션 컴포넌트 추가
import { useNavigate } from 'react-router-dom';
import { useAuth } from "../services/AuthProvider";
import 'react-toastify/dist/ReactToastify.css';
import {toast, ToastContainer} from "react-toastify";

function LoginPage() {

    const { isAuthenticated } = useAuth();
    const navigate = useNavigate();

    useEffect(() => {
        if (isAuthenticated) {
            toast.info("이미 로그인된 상태입니다. 메인 페이지로 이동합니다.", {
                position: "top-center",
                autoClose: 2000, // 3초 후 자동으로 닫힘
                hideProgressBar: true,
                closeOnClick: true,
                pauseOnHover: false,
                draggable: false,
                progress: undefined,
                style: {
                    zIndex: 9999, // 다른 요소보다 높게 설정
                },
            });

            // 토스트 메시지 후 메인 페이지로 리다이렉트
            setTimeout(() => {
                navigate("/react/main");
            }, 2000); // 2초 후 리다이렉트
        }
    }, [isAuthenticated, navigate]);

    return (
        <div style={{position: "relative", width: "100%", height: "100vh", overflow: "hidden"}}>
            {/* Astro 애니메이션 */}
            <AstroAnimation/>

            {/* 로그인 화면 요소 */}
            <div style={{
                position: "absolute",
                top: 0,
                left: 0,
                width: "100%",
                height: "100vh",
                zIndex: 1,
                backgroundColor: "rgba(0, 0, 0, 0.6)",  // 반투명한 검은색 배경으로 애니메이션과 구분
                display: "flex",
                flexDirection: "column",
                alignItems: "center",
                justifyContent: "center",
                color: "#ffffff",  // 텍스트 색상을 흰색으로 변경
            }}>
                <h2>로그인 방법을 선택해주세요</h2>

                {/* Google 로그인 버튼 */}
                {/* 스타일을 인라인에서 설정하지 않고, CSS 클래스 사용 */}
                <GoogleLoginButton className="google-login-button"/>

                {/* ToastContainer 추가 */}
                <ToastContainer />
            </div>
        </div>
    );
}

export default LoginPage;

코드는 이렇다.

후.. 여튼 이제 자잘자잘한거 고쳤으니까 다시 본론. Location을 DB에 집어 넣어야함,,


우선 오류가 나던 이유는 스프링 로그로 찾았다.

여기 보면

user-id-value 어쩌구 저쩌구,,, 블라블라,,

저장하려는 user-id가 String 타입이라 오류남.

이게 그 프론트에서 전송을 하려고 했는데, 잘 생각해보니까 백엔드에서도 로직을 이상하게 짜긴 했다. 현재 로그인한 유저를 구분하고 그 유저의 id 값을 가져오도록 로직 수정함.

CustomOAuth2User에 메서드들을 구현한 것들이 있어서 거기서 getUserId 메서드 추가로 만들어줌.

일단 수정은 했는데 여전히 같은 문제라 sout 찍어봄.

로그 하나하나 다 확인하면서 문제가 되는 부분 고치고 성공함.

일단 뭐가 문제였냐면 이미 해결을 한 상태였는데 위에 처리한 것 때문에,

내가 json 형식 보고 싶어서 HttpServletRequest로 출력해본다고 썼던 코드 때문에 안됐음 ㅋㅋ

그래서 저 부분 지워서 해결했고 결과는

이렇게. 그럼 이제 이걸 처리하면 되는데 아까 로그인 페이지 접근할 때 썼던 토스트를 활용해서 처리하면 자연스럽고 좋을듯.

이런식으로 모두 처리했다.

로그인이 필요하면 알림창 이후 로그인 페이지로 이동하고, 저장시에는 저장 완료 안내후에 메인페이지로 이동하게 설정함!

그럼 이제 이건 일단은 끝 추후 수정이 필요할 것 같긴한데 다음으로 일단 넘어가자.