티스토리 뷰

권한까지 구현되었으니 이제 로그인 기능을 구현해보겠습니다.

 

먼저, 서버 쪽을 하나씩 살펴보면

기본적으로 UserEntity와 UserDTO

그리고 LoginDTO가 있습니다.

 

LoginDTO.java

package com.example.demo.dto;

import lombok.*;

@Getter @Setter
@ToString
@NoArgsConstructor
@Data
public class LoginDTO {
    private String userId;
    private String password;
}

 

UserRepository.java

package com.example.demo.repository;

import com.example.demo.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {   // JpaRepository<Entity 클래스, PK 타입>

	// 로그인
	UserEntity findByUserId(String userId);

}

 

UserService.java

// 로그인
	public JwtToken login(String userId, String password) {
		try{
			// 1. Login ID/PW 를 기반으로 Authentication 객체 생성
			// 이때 authentication 는 인증 여부를 확인하는 authenticated 값이 false
			UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userId, password);

			// 2. 실제 검증 (사용자 비밀번호 체크)이 이루어지는 부분
			// authenticate 매서드가 실행될 때 CustomUserDetailsService 에서 만든 loadUserByUsername 메서드가 실행
			Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);

			// 3. 인증 정보를 기반으로 JWT 토큰 생성
			JwtToken tokenInfo = jwtTokenProvider.generateToken(authentication);

			return tokenInfo; }
            
		catch (Exception e) {
			System.err.println(e);
		}
        
		return null;
	}

 

UserDetailService.java

package com.example.demo.service;

import com.example.demo.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class UserDetailService implements UserDetailsService {

    @Autowired
    UserRepository userRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    // 사용자 이름을 입력받아 해당 사용자의 정보를 검색하고
    // 이를 Spring Security의 UserDetails 객체로 변환하여 반환
    // loadUserByUsername: 로그인 시 실행되는 함수
    @Override
    public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
        return userRepository.findByUserId(userId);
    }

}

 

UserController.java

	@PostMapping("/login")
    // param으로 보내는 경우
//	public JwtToken login(@RequestParam(value="userId") String userId, @RequestParam(value="password") String password) {
//		return userService.login(userId, password);
//	}

	// body에 담아 보내는 경우
	public JwtToken login(@RequestBody LoginDTO loginDTO) {
		String userId = loginDTO.getUserId();
		String password = loginDTO.getPassword();
		JwtToken tokenInfo = userService.login(userId, password);
		return tokenInfo;
	}

 

@RequestParam 어노테이션을 사용하면 아이디와 비밀번호를 각각의 쿼리 파라미터로 전달하면 요청 URL에 파라미터로 전달되게 됩니다.

 

@RequestBody 어노테이션을 사용하면 요청 본문(body)에 JSON 형식으로 아이디와 비밀번호를 담은 객체인 LoginDTO를 전달합니다. 이 경우에는 요청 본문에 JSON 형식으로 데이터가 포함되어 전달됩니다.

 

보안적인 특성상 본문에 담아 전달하겠습니다

 

 


 

다음으로는 클라이언트 측을 살펴보겠습니다.

import React, { useState } from "react";
import '../css/Login.css';
import Nav2 from "../components/Nav2";
import { NavLink, useNavigate } from "react-router-dom";
import axios from "axios";
import { useCookies } from "react-cookie";

let Login = () => {

        // 아이디와 비밀번호 상태
        let [userId, setUserId] = useState("");
        let [password, setPassword] = useState("");

        let navigate = useNavigate();   // useNavigate 훅 사용

		// 아이디 입력 이벤트 핸들러
        let onUserIdHandler = (e) => {
            setUserId(e.target.value)   // 입력된 아이디 상태 업데이트
            console.log("userId: ", e.target.value);
        }

		// 비밀번호 입력 이벤트 핸들러
        let onPasswordHandler = (e) => {
            setPassword(e.target.value)    // 입력된 비밀번호 상태 업데이트
            console.log("password: ", e.target.value);
        }

        // 로그인 함수
        let loginHandler = () => {
            // 로그인 요청 보내기
            axios({
                url: 'http://localhost:8082/api/v1/auth/n/login',  // 로그인 앤드포인트 URL
                method: 'POST',   
                data: {   // 요청 본문에 아이디와 비밀번호 데이터 전송
                    userId: userId,
                    password: password
                }
            })
            .then((res) => {
                // 로그인 성공
                console.log(res.data)
                if (res.status === 200) {   // 응답 상태 코드가 200이면
                    setCookies('user', { userId, password });  // 쿠키에 사용자 정보 저장
                    alert("로그인 성공!");
                    navigate("/");  // 메인페이지로 이동
                    
                } else {
                // 로그인 실패
                    alert("아이디 또는 비밀번호가 잘못되었습니다.");
                    
                }
            })
            .catch((error) => {
                // 서버 오류 또는 네트워크 오류
                alert("로그인에 실패하였습니다.");
                console.error("Error:", error);
            });
        }
        
        // JSX로 로그인 페이지 UI 렌더링
        return (
            <div>
                <div>
                    <Nav2/>   // 네비게이션 컴포넌트 렌더링
                </div>
                <div className="loginForm">
                    <div>
                        <h1 id='login_title'>로그인</h1>
                    </div>
                    <div>
                        <div className="input">
                            <input type="text" className="userId" id="userId" placeholder="아이디" value={userId} onChange={onUserIdHandler} autoFocus></input>
                            <input type="password" className="password" id="password" placeholder="비밀번호" value={password} onChange={onPasswordHandler}></input>
                            <button onClick={loginHandler}>Login</button>
                        </div>
                        <div className="link">
                            <NavLink to="/findId">아이디 찾기</NavLink>
                            <span>&nbsp;|&nbsp;</span>
                            <NavLink to="/findPw">비밀번호 찾기</NavLink>
                            <span>&nbsp;|&nbsp;</span>
                            <NavLink to="/register">회원가입</NavLink>
                        </div>
                    </div>
                </div>
            </div>
        );
    }

export default Login;

 

- useState 훅을 이용해 아이디와 비밀번호 상태를 설정하고 각각의 핸들러를 통해 상태 업데이트를 합니다.

- axios를 사용해 서버와 통신합니다.

- 컨트롤러에서 @RequestParam 을 사용한다면 data가 아닌 params를 써야 합니다.

- 컨트롤러에서 @RequestBody 를 사용한다면 data를 씁니다.

- 응답 상태 코드 (res.status) 가 200이면 로그인 성공이 되어 쿠키에 사용자 정보를 저장하게끔 합니다.

- html이 아니므로 form 태그를 쓰면 안됩니다. 

- 로그인 버튼에 onClick 이벤트로 loginHandler가 연결되게끔 했습니다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함