최원종의 개발 블로그

V13-6 회원 정보 수정 기능 수정 본문

Spring boot 입문/Spring boot 입문 -1

V13-6 회원 정보 수정 기능 수정

chl6698 2026. 6. 9. 10:36


 

User 코드

    // 머스태치 화면에서 사용할 편의 메서드 3
    public boolean isLocal() {
        // true -> 이메일 가입자를 의미 함
        return this.oAuthProvider == OAuthProvider.LOCAL;
    }

detail.mustache 코드

{{> layout/header}}
<div class="container p-5 flex-grow-1">
    <div class="card">
        <div class="card-header">마이프로필</div>
        <div class="card-body">
            <!--  프로필 이미지  start  -->
            <div class="text-center mb-4">
                {{#user.profileImage}}
                <!--  회원 가입시 로컬 가입자                   -->
                    <img class="rounded-circle" src="{{user.profilePath}}" alt="프로필이미지"
                      style="width:200px; height:200px"/>

                   {{#user.local}}
                    <div class="mt-2">
                        <form action="/user/profile-image/delete" method="post">
                            <button type="submit" class="btn btn-sm btn-danger"
                            onclick="return confirm('프로필 사진을 삭제할까요?')">프로필 사진 삭제</button>
                        </form>
                    </div>
                   {{/user.local}}

                {{/user.profileImage}}
                {{^user.profileImage}}
                    <div class="rounded-circle bg-secondary d-inline-flex
                                justify-content-center align-items-center border"
                                    style="width:200px; height:200px;">
                        <span>프로필 사진 없음</span>
                    </div>
                {{/user.profileImage}}
            </div>
            <!--  프로필 이미지   end -->
            <div class="mb-3">
                <label class="form-label fw-bold">사용자명</label>
                <p class="form-control-plaintext">{{user.username}}</p>
            </div>

            <div class="mb-3">
                <label class="form-label fw-bold">이메일</label>
                {{#user.email}}
                    <p class="form-control-plaintext">{{user.email}}</p>
                {{/user.email}}
            </div>

            <div class="mb-3">
                <label class="form-label fw-bold">가입일</label>
                <p class="form-control-plaintext">{{user.createdAt}}</p>
            </div>

            <div class="mb-3">
                <a href="/user/update-form" class="btn btn-primary" >회원정보 수정</a>
                <a href="/board/list" class="btn btn-secondary" >게시글 목록</a>
            </div>

        </div>
    </div>
</div>
{{> layout/footer}}

update-form.mustache 코드

{{> layout/header}}
<div class="container p-5 flex-grow-1">
    <div class="card">
        <div class="card-header"><b>회원정보수정</b></div>
        <div class="card-body">

            <!--  프로필 이미지  start  -->
            <div class="text-center mb-4">
                {{#user.profileImage}}
                    <img class="rounded-circle" src="{{user.profilePath}}" alt="프로필이미지"
                         style="width:200px; height:200px"/>
                   {{#user.local}}
                    <div class="mt-2">
                        <form action="/user/profile-image/delete" method="post">
                            <button type="submit" class="btn btn-sm btn-danger"
                                    onclick="return confirm('프로필 사진을 삭제할까요?')">프로필 사진 삭제</button>
                        </form>
                    </div>
                   {{/user.local}}
                {{/user.profileImage}}
                {{^user.profileImage}}
                    <div class="rounded-circle bg-secondary d-inline-flex
                                justify-content-center align-items-center border"
                         style="width:200px; height:200px;">
                        <span>프로필 사진 없음</span>
                    </div>
                {{/user.profileImage}}
            </div>
            <!--  프로필 이미지   end -->

            <!--  이미지 업로드 시 반드시   multipart/form-data 선언   -->
            <form action="/user/update" method="post" enctype="multipart/form-data">
                <div class="mb-3">
                    <input type="text" class="form-control" name="username" value="{{user.username}}" disabled>
                </div>

                {{#user.local}}
                    <div class="mb-3">
                        <input type="password" class="form-control" name="password" value="" >
                    </div>
                    {{#user.email}}
                    <div class="mb-3">
                        <input type="text" class="form-control" name="email" value="{{user.email}}" disabled>
                    </div>
                    {{/user.email}}

                    <!--  프로필 이미지 등록 필드(선택 사항)  -->
                    <div class="mb-3">
                        <label for="profileImage" class="form-label">프로필 사진(선택사항)</label>
                        <input type="file" id="profileImage" class="form-control" name="profileImage" accept="image/*"  >
                        <small class="form-text text-muted">이미지 파일만 업로드 가능합니다(JPG, PNG, GIF 등)</small>
                        <small class="form-text text-muted d-block mt-1">⭐ 프로필 사진을 등록하지 않아도 회원 가입 가능 </small>
                    </div>

                {{/user.local}}
                <button class="btn btn-primary form-control">회원정보수정</button>
            </form>
        </div>
    </div>
</div>
{{> layout/footer}}

UserController 수정 코드

 UpdateDTO 에 기존 세션에서 가져와 주입한 부분 제거, 및 유효성 검사 제거
    // 회원 정보 수정 기능 요청
    @PostMapping("/user/update")
    public String updateProc(UserRequest.UpdateDTO updateDTO, HttpSession session) {
        // 회원 정보 수정 요청시 기본 비밀번호 null 이고 프로필 이미지만 수정 요청
        User sessionUser = (User) session.getAttribute(Define.SESSION_USER);
        User updateUser = userService.회원정보수정(sessionUser.getId(), updateDTO);
        session.setAttribute(Define.SESSION_USER, updateUser);
        return "redirect:/";
    }

UserService 수정 코드

UserService 에 회원 정보 수정 로직 변경
    /**
     * 사용자 정보 수정 처리 (프로필 업데이트)
     * @param id  (User PK)
     * @param updateDTO (사용자가 요청한 데이터)
     * @return User
     */
    @Transactional
    public User 회원정보수정(Integer id, UserRequest.UpdateDTO updateDTO) {

        String newPassword = null;
        String newProfileImageFilename = null;

        // 1. 항상 조회 부터
        User userEntity = userRepository.findById(id).orElseThrow(
                () -> new Exception404("사용자를 찾을 수 없습니다"));

        // 2. 수정 권한 확인
        if(!userEntity.getId().equals(id)) {
            throw new Exception403("회원정보 수정 권한이 없습니다");
        }

        // 3. 로직 처리 1 - 사용자가 비밀번호를 입력 했을 경우 갱신
        if(updateDTO.getPassword() != null && !updateDTO.getPassword().isBlank()) {
            // 여기서 유효성 검사 해야 됨.
            updateDTO.validate();
            String rawPassword = updateDTO.getPassword();
            updateDTO.setPassword(passwordEncoder.encode(rawPassword));
        } else {
            updateDTO.setPassword(null);
        }

        // 4. 로직 처리 2 - 사용자가 새로운 이미지를 등록했을 경우
        if(updateDTO.getProfileImage() != null && !updateDTO.getProfileImage().isEmpty()) {
            try {
                if(!FileUtil.isImageFile(updateDTO.getProfileImage())) {
                    throw new Exception400("이미지 파일만 업로드 가능합니다");
                }
                // 새 이미지 로컬 폴더에 저장 ( 중복되지 않을 이미지 파일 이름을 리턴)
                newProfileImageFilename = FileUtil.saveFile(updateDTO.getProfileImage(), FileUtil.IMAGES_DIR);
                updateDTO.setProfileImageFileName(newProfileImageFilename);

                // 기존 이미지 파일 삭제 해야 함(로컬에 계속 파일 쌓임)
                String oldProfileImageFileName = userEntity.getProfileImage();
                if(oldProfileImageFileName != null) {
                    FileUtil.deleteFile(oldProfileImageFileName, FileUtil.IMAGES_DIR);
                }
            } catch (IOException e) {
                throw new Exception400("파일 저장에 실패");
            }
        } else {
            updateDTO.setProfileImageFileName(userEntity.getProfileImage());
        }

        // 더티 체킹
        userEntity.update(updateDTO);
        return userEntity;
    }

User 엔티티 수정

User 엔티티에 코드update() 메서드  수정
    // 편의 기능 추가 - 회원 정보 수정
    public void update(UserRequest.UpdateDTO updateDTO) {
        if(updateDTO.getPassword() != null) {
            // 암호화 변경되어 들어 옴
            this.password = updateDTO.getPassword();
        }

        if(updateDTO.getProfileImageFileName() != null ) {
            // 신규 파일 생성해서 들어 옴
            this.profileImage = updateDTO.getProfileImageFileName();
        }
    }