[Photogram] 회원정보 수정

2022. 7. 9. 11:57·🚗 Backend Toy Project/Photogram

📝 View

  • 회원정보 수정 기능을 본격적으로 구현해보기 전에 먼저 회원정보 변경 창에 생성되어 있는 모든 입력란에 현재 접속중인 사용자의 정보가 들어가 있을 수 있도록 하려고 합니다.
  • 이를 위해 먼저 View 페이지에 아래와 같은 코드를 추가해주었습니다.
<sec:authorize access="isAuthenticated()">
    <sec:authentication property="principal" var="principal"/>
</sec:authorize>
  • 위 코드를 통해 우리는 현재 접속중인 사용자에 대한 정보를 'principal'이라는 변수를 통해 ${principal.user.id}와 같이 접근할 수 있습니다.
<!--프로필 수정-->
<form id="profileUpdate" onsubmit="update(${principal.user.id}, event)">
    <div class="content-item__02">
        <div class="item__title">이름</div>
        <div class="item__input">
            <input type="text" name="name" placeholder="이름"
                value="${principal.user.name}" required="required"/>
        </div>
    </div>
    <div class="content-item__03">
        <div class="item__title">유저네임</div>
        <div class="item__input">
            <input type="text" name="username" placeholder="유저네임"
                value="${principal.user.username}" readonly="readonly" />
        </div>
    </div>
    <div class="content-item__04">
        <div class="item__title">패스워드</div>
        <div class="item__input">
            <input type="password" name="password" placeholder="패스워드" required="required"/>
        </div>
    </div>
    <div class="content-item__05">
        <div class="item__title">웹사이트</div>
        <div class="item__input">
            <input type="text" name="website" placeholder="웹 사이트"
                value="${principal.user.website}" />
        </div>
    </div>
    <div class="content-item__06">
        <div class="item__title">소개</div>
        <div class="item__input">
            <textarea name="bio" id="" rows="3">${principal.user.bio}</textarea>
        </div>
    </div>
    <div class="content-item__07">
        <div class="item__title"></div>
        <div class="item__input">
            <span><b>개인정보</b></span> <span>비즈니스나 반려동물 등에 사용된 계정인 경우에도
                회원님의 개인 정보를 입력하세요. 공개 프로필에는 포함되지 않습니다.</span>
        </div>
    </div>
    <div class="content-item__08">
        <div class="item__title">이메일</div>
        <div class="item__input">
            <input type="text" name="email" placeholder="이메일"
                value="${principal.user.email}" readonly="readonly" />
        </div>
    </div>
    <div class="content-item__09">
        <div class="item__title">전화번호</div>
        <div class="item__input">
            <input type="text" name="phone" placeholder="전화번호"
                value="${principal.user.phone}" />
        </div>
    </div>
    <div class="content-item__10">
        <div class="item__title">성별</div>
        <div class="item__input">
            <input type="text" name="gender" value="${principal.user.gender}" />
        </div>
    </div>

    <!--제출버튼-->
    <div class="content-item__11">
        <div class="item__title"></div>
        <div class="item__input">
            <button>제출</button>
        </div>
    </div>
    <!--제출버튼end-->

</form>
<!--프로필수정 form end-->

📝 JavaScript

  • View 단의 코드를 보면 회원정보 수정 창의 '제출' 버튼을 클릭할 경우 update() 함수가 호출되도록 하고 있는 것을 볼 수 있는데, 해당 함수는 아래와 같이 구현되어 있습니다.
// (1) 회원정보 수정
function update(user_id, event) {
    event.preventDefault();    //form 태그 액션 막기
    let data = $("#profileUpdate").serialize();

    $.ajax({
        type:"put",
        url:`/api/user/${user_id}`,
        data: data,
        contentType: "application/x-www-form-urlencoded; charset=utf-8",
        dataType: "json"
    }).done(resp => {    //Http status: 2xx
        location.href=`/user/${user_id}`;
    }).fail(error => {
        if(error.data == null)
            alert(error.responseJSON.message);
        else 
            alert(JSON.stringify(error.responseJSON.data));
    })
}
  • 여기서 event.preventDefault(); 구문은 form 태그의 액션으로 인해 ajax 함수가 무시되는 것을 방지하기 위함입니다.
  • serialize() 함수는 form 태그 안에 있는 모든 입력 데이터(username, password, ...)를 하나의 변수에 한꺼번에 저장하기 위해 사용하였습니다.

📝 Controller

  • 컨트롤러에는 위에 설정되어 있는 경로를 요청받아 회원 정보 수정을 수행할 수 있도록 아래와 같이 함수를 구현해주었습니다.
package com.cos.photogram.web.api;

...

@RequiredArgsConstructor
@RestController
public class UserApiController {

    private final UserService userService;

    @PutMapping("/api/user/{id}")
    public CMRespDto<?> update(
            @PathVariable Long id, 
            @Valid UserUpdateDto userUpdateDto,
            BindingResult bindingResult,
            @AuthenticationPrincipal PrincipalDetails principalDetails) {

        if (bindingResult.hasErrors()) {
            Map<String, String> errorMap = new HashMap<>();

            for (FieldError error : bindingResult.getFieldErrors()) {
                errorMap.put(error.getField(), error.getDefaultMessage());
            }

            throw new CustomValidationApiException("유효성 검사에 실패하였습니다.", errorMap);
        }

        User userEntity = userService.update(id, userUpdateDto.toEntity());
        principalDetails.setUser(userEntity);

        return new CMRespDto<>(1, "회원수정 완료", userEntity);
    }
}
package com.cos.photogram.web.dto.user;

...

@Data
public class UserUpdateDto {

    @NotBlank
    private String name;

    @NotBlank
    private String password;

    private String website;
    private String bio;
    private String phone;
    private String gender;

    public User toEntity() {
        return User.builder()
                .name(name)
                .password(password)
                .website(website)
                .bio(bio)
                .phone(phone)
                .gender(gender)
                .build();
    }
}

📝 Service

  • 서비스 단에서는 영속화 및 더티 체킹을 통한 회원정보 수정 로직을 수행합니다.
package com.cos.photogram.service;

...

@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    @Transactional
    public User update(Long id, User user) {
        User userEntity = userRepository.findById(id).orElseThrow(() -> {
            return new CustomValidationApiException("회원정보 수정 실패: 회원 ID를 찾을 수 없습니다.");
        });

        userEntity.setName(user.getName());
        userEntity.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
        userEntity.setWebsite(user.getWebsite());
        userEntity.setBio(user.getBio());
        userEntity.setPhone(user.getPhone());
        userEntity.setGender(user.getGender());

        return userEntity;
    }
}
  • 여기서 보면 데이터베이스 단에서 오류가 발생할 경우를 대비하여 orElseThrow() 함수에서 예외를 내보내고 있는데, 이를 기존에 우리가 구현해놓았던 CustomValidationApiException 클래스를 재사용하고 있습니다. 따라서 이를 위해 해당 클래스에 새로운 생성자를 추가해주었습니다.
package com.cos.photogram.handler.ex;

...

public class CustomValidationApiException extends RuntimeException {

    ...

    //추가된 부분
    public CustomValidationApiException(String message) {
        super(message);
    }

    public CustomValidationApiException(String message, Map<String, String> erorrMap) {
        super(message);
        this.erorrMap = erorrMap;
    }

    ...
}

📝 ExceptionHandler

  • 다음으로 이전 시간에 구현해놓았던 ExceptionHandler를 조금 수정해주었습니다.
package com.cos.photogram.handler;

...

@RestController
@ControllerAdvice
public class ControllerExceptionHandler {

    @ExceptionHandler(CustomValidationException.class)
    public String validationException(CustomValidationException e) {
        return Script.back(e.getErorrMap().toString());
    }

    @ExceptionHandler(CustomValidationApiException.class)
    public ResponseEntity<?> validationApiException(CustomValidationApiException e) {
        return new ResponseEntity<>(new CMRespDto<>(-1, e.getMessage(), e.getErorrMap()), HttpStatus.BAD_REQUEST);
    }
}
  • 이렇게 한 이유는 ajax를 사용할 시 Http 상태 코드에 따라서 done() 또는 fail() 함수가 호출되게 되는데, 이를 위해서 ResponseEntity를 활용하여 기존의 Response DTO와 함께 HttpStatus도 함께 반환해준 것입니다.
}).done(resp => {    //Http status: 2xx
    location.href=`/user/${user_id}`;
}).fail(error => {
    if(error.data == null)
        alert(error.responseJSON.message);
    else 
        alert(JSON.stringify(error.responseJSON.data));
})

 

GitHub - Daegwon-Kim/SpringBoot-Photogram

Contribute to Daegwon-Kim/SpringBoot-Photogram development by creating an account on GitHub.

github.com

 

저작자표시 (새창열림)

'🚗 Backend Toy Project > Photogram' 카테고리의 다른 글

[Photogram] 프로필 페이지 - 서버에 이미지 업로드  (0) 2022.07.10
[Photogram] 구독하기  (0) 2022.07.10
[Photogram] 로그인  (0) 2022.07.08
[Photogram] 회원가입 - 공통 응답 DTO, Script 만들기  (0) 2022.07.08
[Photogram] 회원가입 - Validation  (0) 2022.07.07
'🚗 Backend Toy Project/Photogram' 카테고리의 다른 글
  • [Photogram] 프로필 페이지 - 서버에 이미지 업로드
  • [Photogram] 구독하기
  • [Photogram] 로그인
  • [Photogram] 회원가입 - 공통 응답 DTO, Script 만들기
Baeg-won
Baeg-won
  • Baeg-won
    좋았다면 추억이고 나빴다면 경험이다.
    Baeg-won
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 🍃 Spring, Spring Boot
        • 스프링 프레임워크 기초
        • 스프링 핵심 원리 - 기본편
        • 자바 ORM 표준 JPA 프로그래밍 - 기본편
        • 스프링 MVC
        • 실전! 스프링 부트와 JPA 활용1 - 웹 애플리..
      • 🥑 Web Technoloy
      • 🚗 Backend Toy Project
        • 스프링 부트 게시판
        • Photogram
        • Baeg-won Clothing Gallery
      • 🥇 Problem Solving
        • Breadth-First Search
        • Depth-First Search
        • Backtracking
        • Simulation
        • Two-pointer
        • Binary Search
        • Greedy
        • Dynamic Programming
        • Minimum Spanning Tree
        • Dijkstra
        • Floyd warshall
      • ☕ Java
        • 명품 자바 에센셜
        • Applications
      • 🍦 JavaScript
        • JavaScript 기초
      • 🐧 Linux
        • 이것이 리눅스다(CentOS 8)
      • 📟 Database
        • 혼자 공부하는 SQL
      • 🧬 Data Structure
      • 🎬 HTML
      • 🎤 Tech Interview
      • 📌 etc
        • Unity 2D Raising Jelly Game
        • C++
        • 영어 쉐도잉
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Baeg-won
[Photogram] 회원정보 수정
상단으로

티스토리툴바