개인프로젝트/기능프로그램_오늘뭐입지

20240509_JPA기반 / 로그인 기능 구현

일일일코_장민기 2024. 5. 10. 23:45
728x90

Controller

package org.member.memberController;

import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.member.MemberDTO;
import org.member.MemberDTO;
import org.member.MemberVO;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Controller
@RequestMapping("/member")
public class MemberController {

    private final MemberService memberService;

    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }

    @PostMapping("/create")
    public ModelAndView createMember(MemberDTO createMemberData, HttpSession session) throws Exception{

        log.info(createMemberData.toString());

        //아이디 중복 검사
        if (memberService.existsByUsername(createMemberData.getUsername())) {
            throw new Exception();
        }
        //이메일 중복 검사
        if (memberService.existsByEmail(createMemberData.getEmail())) {
            throw new Exception();
        }
        //회원가입 진행
        memberService.createMember(createMemberData);

        ModelAndView view = new ModelAndView();
            view.addObject("message", "회원가입 성공");
            session.setAttribute("memberDTO", createMemberData);
            view.setViewName("Member/MemberPage");
        return view;
    }

    @PostMapping("/login")
    public ModelAndView readMemberByUsernmaeAndPassword(String username, String password, HttpSession session) {
        ModelAndView modelAndView = new ModelAndView();
        Optional<MemberDTO> member = memberService.readMemberByUsernameAndPassword(username, password);
        if(member.isPresent()){
            MemberDTO memberDTO = new MemberDTO(member.get().getId(), member.get().getUsername(), member.get().getPassword(), member.get().getEmail());
            session.setAttribute("memberDTO", memberDTO);
            modelAndView.addObject("memberDTO", memberDTO);
            modelAndView.setViewName("Member/MemberPage");
            return modelAndView;
        }
        modelAndView.setViewName("Member/LoginPage");
        return modelAndView;
    }

    @PostMapping("logout")
    public ModelAndView logout(HttpSession session) {
        session.removeAttribute("memberDTO");
        ModelAndView view = new ModelAndView();
        view.addObject("message", "로그아웃 성공");
        view.setViewName("Member/LoginPage");
        return view;
    }

    @PostMapping("/updateMember")
    public void updateMember(Long id, String email) throws Exception {
        //이메일 중복 검사
        if (!memberService.existsByEmail(email)) {
            throw new Exception();
        }
        //데이터 변경 진행
        memberService.updateMember(id, email);
    }

    @PostMapping("/deleteMember")
    public void deleteMember(Long id) {
        memberService.deleteMember(id);
    }
}

 

Service

package org.member.memberController;

import org.member.MemberVO;
import org.member.MemberDTO;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
public class MemberService {

    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    public void createMember(MemberDTO createMemberDataDTO) {
        MemberVO member = new MemberVO();
            member.setUsername(createMemberDataDTO.getUsername());
            member.setPassword(createMemberDataDTO.getPassword());
            member.setEmail(createMemberDataDTO.getEmail());
        memberRepository.save(member);
    }

    public Optional<MemberDTO> readMemberByUsernameAndPassword(String username, String password) {
        return Optional.ofNullable(memberRepository.findByUsernameAndPassword(username, password));
    }

    public void updateMember(Long id, String email) {
        MemberVO updateMember = memberRepository.findById(id).orElseThrow(() -> new RuntimeException("Member not found"));
            updateMember.setEmail(email);
        memberRepository.save(updateMember);
    }

    public void deleteMember(Long id) {
        memberRepository.deleteById(id);
    }

    //검사용 코드
    public boolean existsByUsername(String username) {
        return memberRepository.existsByUsername(username);
    }
    public boolean existsByEmail(String email) {
        return memberRepository.existsByEmail(email);
    }
}

 

Repository

package org.member.memberController;

import org.member.MemberDTO;
import org.member.MemberVO;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MemberRepository extends JpaRepository<MemberVO, Long> {

    MemberDTO findByUsernameAndPassword(String username, String password);

    boolean existsByUsername(String username);
    boolean existsByEmail(String email);
}

 

MemberDTO

package org.member;

import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.Value;
import org.springframework.context.annotation.Primary;

import java.io.Serializable;

/**
 * {@link MemberVO}와 관련된 DTO
 */
@Data
@Value
public class MemberDTO implements Serializable {
    @NotNull
    Integer id;

    @Size(max = 50)
    @NotBlank(message = "아이디를 입력하세요")
    String username;

    @Size(max = 100)
    @NotBlank(message = "비밀번호를 입력하세요")
    String password;

    @Size(max = 100)
    @Email(message = "올바른 이메일 형식이 아닙니다")
    @NotBlank(message = "이메일을 입력하세요")
    String email;
}

 

MemberVO

package org.member;

import jakarta.persistence.*;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
import org.springframework.format.annotation.DateTimeFormat;

import java.time.Instant;

@Getter
@Setter
@Entity                         //JPA가 관리하는 클래스
@Table(name = "membervo")       //테이블과 매핑할 테이블은 해당 어노테이션을 붙임
public class MemberVO {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @ColumnDefault("nextval('users_userid_seq'")
    @Column(name = "userid", nullable = false)
    private Integer id;

    @Size(max = 50)
    @NotNull
    @Column(name = "username", nullable = false, length = 50)
    private String username;

    @Size(max = 100)
    @NotNull
    @Column(name = "password", nullable = false, length = 100)
    private String password;

    @Size(max = 100)
    @NotNull
    @Column(name = "email", nullable = false, length = 100)
    @Email
    private String email;

    @ColumnDefault("CURRENT_TIMESTAMP")
    @Column(name = "registerdate")
    @DateTimeFormat
    private Instant registerdate;

}

 

RegistPage.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
    <script type="text/javascript" src="<c:url value="/resources/js/RegisterPage.js"/>"></script>

</head>
<body>

<h1>Main Register Page</h1>
<form method="post" action="<c:url value='/member/create'/>">
    username: <input type="text" id="username" name="username"><br>
    <span id="existUsername"></span>
    password: <input type="text" id="password" name="password"><br>
    email: <input type="email" id="email" name="email"><br>
    <span id="existEmail"></span>
    <input type="submit" value="회원가입">
</form>
<p>
    <a href="<c:url value='/LoginPage'/>">로그인 페이지</a>
</p>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
        crossorigin="anonymous"></script>
</body>
</html>

 

LoginPage.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">

</head>
<body>

<h1>Main Login Page</h1>
<form method="post" action="<c:url value='/member/login'/>">
    username: <input type="text" name="username"><br>
    password: <input type="text" name="password"><br>
    <input type="submit" value="로그인">
</form>
<p>
    <a href="<c:url value='/RegisterPage'/>">회원가입 페이지</a>
</p>
<p>
    <a href="<c:url value='/UpdateEmailPage'/>">이메일 업데이트 페이지</a>
</p>
<p>
    <a href="<c:url value='/DeletePage'/>">회원 삭제 페이지</a>
</p>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
        crossorigin="anonymous"></script>
</body>
</html>

 

MemberPage.js

$(function(){

    // 현재 옷 출력
    viewMyClothes()

})//$(function()

function viewMyClothes(){
    var allClothesField = $("#allClothesField")
    $.ajax({
        type: "post",
        url: "/selectAllClothes",
        success: function(response){
            var tableHtml = "<table>";
            tableHtml += "<tr><th>Category</th><th>Cloth</th></tr>";
            response.forEach(function(cloth){
                tableHtml += "<tr>";
                tableHtml +=    "<td>" + cloth.category + "</td>";
                tableHtml +=    "<td>" + cloth.clothdata + "</td>";
                tableHtml += "</tr>";
            });
            tableHtml += "</table>";

            // 생성된 표를 화면에 출력
            allClothesField.html(tableHtml);
        },
        error: function(){
            console.log("저장된 옷 불러오기 실패")
        }
    })
}

- 보유 중인 옷을 출력하는 코드

- 토글 형태로 실시간으로 표가 변화되는 코드를 짜고 싶어서 HTML코드로 짰는데, 이 페이지에는 맞지 않다.

- 수정이 필요한 부분

 

 

 

다만 JPA를 사용중이니 서비스와 Repository 사이에서 entity로 넘어가게 되긴 한다

 

Security도 사용하지 않은 간단한 Crud이지만, entity와 jpa와 postgre가 연계되니 애를 먹었다...;;

 

특히 postgre의 sequence와 table의 권한을 일일히 줘야 하는게 맞는가 싶다

분명 방법이 있을텐데...

 

또한 jpa 특성 상 회원가입을 한다고 해서 바로 DB에 들어가는게 아니라 DB로 보내는 쿼리가 없으면 저장까지 딜레이가 발생한다.

--> id발급이 늦어서 발생하는 문제가 있을까? (현재 미지수)

 

 

 

 

 

 

# 개선이 필요한 부분

- postgre 권한 부여 문제

- MemberPage.js 함수 효율화