Spring boot

2024.10.10 Blog 프로젝트 만들기(JPA) 회원 정보 수정

정훈5 2024. 10. 10. 11:47

 

학습 목표 

1. 회원 수정 DTO(Data Transfer Object)를 생성할 수 있다.
2. 회원수정 컨트롤러를 생성하고 세션을 동기화할 수 있다.
3. 회원 수정 쿼리( JPA API) 를 작성할 수 있다.

 

UserController.java 코드 추가

더보기
/**
     * 회원 정보 수정
     * @param reqDTO
     * @return
     */
    @PostMapping("/user/update")
    public String update(@ModelAttribute(name = "updateDTO") UserDTO.UpdateDTO reqDTO) {
        User sessionUser = (User) session.getAttribute("sessionUser");
        if (sessionUser == null) {
            return "redirect:/login-form";
        }
        // 유효성 검사는 생략
        // 사용자 정보 수정
        User updatedUser = userRepository.updateById(sessionUser.getId(), reqDTO.getPassword(), reqDTO.getEmail());
        // 세션 정보 동기화 처리
        session.setAttribute("sessionUser", updatedUser);
        return "redirect:/";
    }

    /**
     * 회원 정보 페이지 요청
     * 주소 설계 : http://localhost:8080/user/update-form
     *
     * @param
     * @return 문자열
     * 반환되는 문자열을 뷰 리졸버가 처리하면
     * 머스태치 템플릿 엔진을 통해서 뷰 파일을 렌더링 합니다.
     */
    @GetMapping("/user/update-form")
    public String updateForm(HttpServletRequest request) {

        log.info("회원 수정 페이지");
       // model.addAttribute("name", "회원 수정 페이지");

        User sessionUser = (User)session.getAttribute("sessionUser");
        if(sessionUser == null) {
            return "redirect:/login-form";
        }
        User user = userRepository.findById(sessionUser.getId());
        request.setAttribute("user", user);

        return "user/update-form"; // 템플릿 경로: user/update-form/mustache
    }

 

UserRepository.java 코드 추가

Repository 계층에서는 DTO(Data Transfer Object)를 사용하지 않는 것이 권장됩니다.

DTO는 외부와의 데이터 교환을 위한 객체이며, 대신, 엔티티(Entity) 를 직접 다루는 것이 더 적합하다.

즉, 계층 간의 책임을 명확히 구분 하기 위함 이고 리포지토리는 비즈니스 로직을 수행하지 않고 데이터 저장 및 조회에 집중 한다.

 

더보기
/**
     * @param id
     * @param password
     * @param email
     * @return
     */
    @Transactional
    // 사용자 정보 수정 기능 만들기
    public User updateById(int id,String password, String email) {

        // id 값으로 영속성 컨텍스트 정보가 있는가? 확인
        User userEntity = findById(id);
        userEntity.setPassword(password);
        userEntity.setEmail(email);
        
        return userEntity; // 수정

    }

    public User findById(int id) {
        return em.find(User.class, id);
    }

 

update-form.mustache 코드 수정

 

더보기
{{> layout/header}}

<main class="container p-5 content">
    <div class="card">
        <div class="card-header">회원정보 수정</div>
        <div class="card-body">
            <form action="/user/update" method="post" enctype="application/x-www-form-urlencoded">
                <div class="mb-3">
                    <input type="text" class="form-control" placeholder="enter username" name="username" disabled value="{{user.username}}">
                </div>

                <div class="mb-3">
                    <input type="password" class="form-control" placeholder="enter password" name="password">
                </div>

                <div class="mb-3">
                    <input type="email" class="form-control" placeholder="enter email" name="email" value="{{user.email}}">
                </div>

                <button type="submit" class="btn btn-primary form-control">회원정보수정</button>
            </form>

        </div>
    </div>

</main>

{{> layout/footer}}

 

UserController.java 전체코드

더보기
package com.tenco.blog_v1.user;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

@Slf4j
@Controller
@RequiredArgsConstructor
public class UserController {
    
    // DI 처리
    private final UserRepository userRepository;
    private final HttpSession session;

    /**
     * 회원 정보 수정
     * @param reqDTO
     * @return
     */
    @PostMapping("/user/update")
    public String update(@ModelAttribute(name = "updateDTO") UserDTO.UpdateDTO reqDTO) {
        User sessionUser = (User) session.getAttribute("sessionUser");
        if (sessionUser == null) {
            return "redirect:/login-form";
        }
        // 유효성 검사는 생략
        // 사용자 정보 수정
        User updatedUser = userRepository.updateById(sessionUser.getId(), reqDTO.getPassword(), reqDTO.getEmail());
        // 세션 정보 동기화 처리
        session.setAttribute("sessionUser", updatedUser);
        return "redirect:/";
    }

    /**
     * 회원 정보 페이지 요청
     * 주소 설계 : http://localhost:8080/user/update-form
     *
     * @param
     * @return 문자열
     * 반환되는 문자열을 뷰 리졸버가 처리하면
     * 머스태치 템플릿 엔진을 통해서 뷰 파일을 렌더링 합니다.
     */
    @GetMapping("/user/update-form")
    public String updateForm(HttpServletRequest request) {

        log.info("회원 수정 페이지");
       // model.addAttribute("name", "회원 수정 페이지");

        User sessionUser = (User)session.getAttribute("sessionUser");
        if(sessionUser == null) {
            return "redirect:/login-form";
        }
        User user = userRepository.findById(sessionUser.getId());
        request.setAttribute("user", user);

        return "user/update-form"; // 템플릿 경로: user/update-form/mustache
    }

    /**
     * 회원 가입 기능 요청
     * @param reqDto
     * @return
     */
    @PostMapping("/join")
    public String join(@ModelAttribute(name = "joinDTO") UserDTO.JoinDTO reqDto) {
        userRepository.save(reqDto.toEntity());

        return "redirect:/login-form";
    }

    /**
     * 회원가입 페이지 요청
     * 주소 설계 : http://localhost:8080/join-form
     *
     * @param model
     * @return 문자열
     * 반환되는 문자열을 뷰 리졸버가 처리하면
     * 머스태치 템플릿 엔진을 통해서 뷰 파일을 렌더링 합니다.
     */
    @GetMapping("/join-form")
    public String joinForm(Model model) {

        log.info("회원가입 페이지");
        model.addAttribute("name", "회원가입 페이지");
        return "user/join-form"; // 템플릿 경로: user/join-form/mustache
    }

    /**
     * 자원에 요청은 GET 방식이지만 보안의 이유로 예외!
     * 로그인 처리 메서드
     * 요청 주소 : POST  http://localhost:8080/login
     * @param reqDTO
     * @return
     */
    @PostMapping("/login")
    public String login(UserDTO.LoginDTO reqDTO) {
        try {
            User sessionUser = userRepository.findByUserNameAndPassword(reqDTO.getUsername(), reqDTO.getPassword());
            session.setAttribute("sessionUser", sessionUser);
            return "redirect:/";
        } catch (Exception e) {
            // 쿼리 스트링으로 error 이 들어오면 따로 처리하는 방식도 있다.
            // 로그인 실패
            return "redirect:/login-form?error";
        }
    }

    /**
     * 로그아웃
     * 주소 : http://localhost:8080/logout
     * @return
     */
    @GetMapping("/logout")
    public String logout() {
        session.invalidate(); // 세션을 무효화 한다. (로그아웃)
        return "redirect:/";
    }



    /**
     * 로그인 페이지 요청
     * 주소 설계 : http://localhost:8080/login-form
     *
     * @param model
     * @return 문자열
     * 반환되는 문자열을 뷰 리졸버가 처리하면
     * 머스태치 템플릿 엔진을 통해서 뷰 파일을 렌더링 합니다.
     */
    @GetMapping("/login-form")
    public String loginForm(Model model) {

        log.info("로그인 페이지");
        model.addAttribute("name", "로그인 페이지");
        return "user/login-form"; // 템플릿 경로: user/login-form/mustache
    }

}

 

UserRepository.java 전체코드

더보기
package com.tenco.blog_v1.user;

import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
@RequiredArgsConstructor
public class UserRepository {

    private final EntityManager em;

    /**
     * 사용자 저장 메서드 (JPA API 사용)
     * @param user
     * @return 저장된 사용자 엔티티
     */
    @Transactional
    public User save(User user) {
        // JPQL은 INSERT 구문을 직접 지원하지 않습니다.
        em.persist(user); // 영속화
        return user;
    }

    /**
     * 사용자 이름과 비밀번호로 사용자 조회
     * @param username
     * @param password
     * @return 조회된 User 엔티티, Null
     */
    public User findByUserNameAndPassword(String username, String password) {
        TypedQuery<User> jpql =
                em.createQuery("SELECT u FROM User u WHERE u.username = :username AND u.password = :password", User.class);
        jpql.setParameter("username", username);
        jpql.setParameter("password", password);

        return jpql.getSingleResult();
    }

    /**
     * @param id
     * @param password
     * @param email
     * @return
     */
    @Transactional
    // 사용자 정보 수정 기능 만들기
    public User updateById(int id,String password, String email) {

        // id 값으로 영속성 컨텍스트 정보가 있는가? 확인
        User userEntity = findById(id);
        userEntity.setPassword(password);
        userEntity.setEmail(email);
        
        return userEntity; // 수정

    }

    public User findById(int id) {
        return em.find(User.class, id);
    }

   

}

 

http://localhost:8080/h2-console 에 들어가서 기존 회원 비밀번호를 확인한다.

 

로그인을 한다.

 

회원정보보기를 클릭한다

 

 

회원정보에서 비밀번호를 1111 로 변경한다.

 

http://localhost:8080/h2-console 에 들어가서 길동의 비밀번호가 변경되었는지 확인한다.