본문 바로가기
스프링과 JPA 기반 웹 어플리케이션 개발/1부 (개발환경, 회원가입, 로그인, 계정설정)

30. 닉네임 수정

by Backchus 2020. 4. 22.

계정 관리 뷰

 

닉네임 수정

  • 닉네임은 특정 패턴("^[ㄱ-ㅎ가-힣a-z0-9_-]{3,20}$")의 문자열만 지원 함.
  • 중복 닉네임 확인.

 

settings패키지 재구성

settings 패키지

 

nickname 수정시 데이터를 전달 할 Nickname DTO 생성

package me.weekbelt.studyolle.settings.form;

@Data
public class NicknameForm {

    @NotBlank
    @Length(min = 3, max = 20)
    @Pattern(regexp = "^[ㄱ-ㅎ가-힣a-z0-9_-]{3,20}$")
    private String nickname;
}

 

올바른 nickname 입력을 검증하기위한 NicknameValidator 생성

package me.weekbelt.studyolle.settings.validator;

@RequiredArgsConstructor
@Component
public class NicknameFormValidator implements Validator {
    
    private final AccountRepository accountRepository;
    
    @Override
    public boolean supports(Class<?> aClass) {
        return NicknameForm.class.isAssignableFrom(aClass);
    }

    @Override
    public void validate(Object target, Errors errors) {
        NicknameForm nicknameForm = (NicknameForm) target;
        Account byNickname = accountRepository.findByNickname(nicknameForm.getNickname());
        if (byNickname != null) {
            errors.rejectValue("nickname", "wrong.value", "입력하신 닉네임을 사용할 수 없습니다.");
        }
    }
}

 

nickname 변경을 위한 핸들러 추가

package me.weekbelt.studyolle.settings;

@RequiredArgsConstructor
@Controller
public class SettingsController {

    // 기존 코드 ....
    
    private final NicknameFormValidator nicknameFormValidator;

    // 기존 코드 ....

    @InitBinder("nicknameForm")
    public void nicknameFormInitBinder(WebDataBinder webDataBinder) {
        webDataBinder.addValidators(nicknameFormValidator);
    }

    // 기존 코드 ....

    @GetMapping("/settings/account")
    public String updateAccountForm(@CurrentUser Account account, Model model) {
        model.addAttribute(account);
        model.addAttribute(modelMapper.map(account, NicknameForm.class));
        return "settings/account";
    }

    @PostMapping("/settings/account")
    public String updateAccount(@CurrentUser Account account, @Valid NicknameForm nicknameForm,
                                Errors errors, Model model, RedirectAttributes attributes) {
        if (errors.hasErrors()) {
            model.addAttribute(account);
            return "settings/account";
        }

        accountService.updateNickname(account, nicknameForm.getNickname());
        attributes.addFlashAttribute("message", "닉네임을 수정했습니다.");
        return "redirect:" + "/settings/account";
    }
}

 

nickname 변경 처리를 위한 updateNickname메소드 생성

package me.weekbelt.studyolle.account;

@Transactional
@RequiredArgsConstructor
@Service
public class AccountService implements UserDetailsService {

    // 기존 코드

    public void updateNickname(Account account, String nickname) {
        account.setNickname(nickname);
        accountRepository.save(account);

        // login메소드를 추가해주는 이유는 navigation에서 dropdown에서의 nickname이 바뀌도록 해주기 위함이다.
        login(account);
    }
}

 

닉네임 변경 뷰인 settings/account.html 생성

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments.html :: head"></head>
<body class="bg-light">
<nav th:replace="fragments.html :: main-nav"></nav>
<div class="container">
    <div class="row mt-5 justify-content-center">
        <div class="col-2">
            <div th:replace="fragments.html :: settings-menu(currentMenu='account')"></div>
        </div>
        <div class="col-8">
            <div th:if="${message}" class="alert alert-info alert-dismissible fade show mt-3" role="alert">
                <span th:text="${message}">완료</span>
                <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="row">
                <h2 class="col-12">닉네임 변경</h2>
            </div>
            <div class="row">
                <form class="needs-validation col-12" th:object="${nicknameForm}" th:action="@{/settings/account}" method="post" novalidate>
                    <div class="alert alert-warning" role="alert">
                        닉네임을 변경하면 프로필 페이지 링크도 바뀝니다!
                    </div>
                    <div class="form-group">
                        <input id="nickname" type="text" th:field="*{nickname}" class="form-control" aria-describedby="nicknameHelp" required>
                        <small id="nicknameHelp" class="form-text text-muted">
                            공백없이 문자와 숫자로만 3자 이상 20자 이내로 입력하세요. 가입후에 변경할 수 있습니다.
                        </small>
                        <small class="invalid-feedback">닉네임을 입력하세요.</small>
                        <small class="form-text text-danger" th:if="${#fields.hasErrors('nickname')}" th:errors="*{nickname}">nickname Error</small>
                    </div>
                    <div class="form-group">
                        <button class="btn btn-outline-primary" type="submit" aria-describedby="submitHelp">변경하기</button>
                    </div>
                </form>
            </div>
            <div class="dropdown-divider"></div>
            <div class="row">
                <div class="col-sm-12">
                    <h2 class="text-danger">계정 삭제</h2>
                    <div class="alert alert-danger" role="alert">
                        이 계정은 삭제할 수 없습니다.
                    </div>
                    <button class="btn btn-outline-danger disabled">계정 삭제</button>
                </div>
            </div>
        </div>
    </div>

    <div th:replace="fragments.html :: footer"></div>
</div>
<script th:replace="fragments.html :: form-validation"></script>
</body>
</html>

 

테스트 코드 작성

package me.weekbelt.studyolle.settings;
t static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringBootTest
@AutoConfigureMockMvc
class SettingsControllerTest {

    // 기존 코드 ....
    
    @WithAccount("joohyuk")
    @DisplayName("닉네임 수정 폼")
    @Test
    public void updateAccountForm() throws Exception {
        mockMvc.perform(get("/settings/account"))
                .andExpect(status().isOk())
                .andExpect(model().attributeExists("account"))
                .andExpect(model().attributeExists("nicknameForm"));

    }

    @WithAccount("joohyuk")
    @DisplayName("닉네임 수정하기 - 입력값 정상")
    @Test
    public void updateAccount_success() throws Exception {
        String newNickname = "weekbelt";
        mockMvc.perform(post("/settings/account")
                .param("nickname", newNickname)
                .with(csrf()))
                .andExpect(status().is3xxRedirection())
                .andExpect(redirectedUrl("/settings/account"))
                .andExpect(flash().attributeExists("message"));

        assertThat(accountRepository.findByNickname(newNickname)).isNotNull();

    }
}

참고: https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-JPA-%EC%9B%B9%EC%95%B1#

 

스프링과 JPA 기반 웹 애플리케이션 개발 - 인프런

이 강좌에서 여러분은 실제로 운영 중인 서비스를 스프링, JPA 그리고 타임리프를 비롯한 여러 자바 기반의 여러 오픈 소스 기술을 사용하여 웹 애플리케이션을 개발하는 과정을 학습할 수 있습니다. 이 강좌를 충분히 학습한다면 여러분 만의 웹 서비스를 만들거나 취직에 도움이 될만한 포트폴리오를 만들 수 있을 겁니다. 활용 웹 개발 프레임워크 및 라이브러리 Java Spring Spring Boot Spring Data JPA Thymeleaf 온라인 강의 스

www.inflearn.com