항해99/개발일지

20220131 개발일지 #@Valid

paran21 2022. 2. 1. 22:51

오늘도 개인 과제를 위주로 하였다.

 

우선 로그인 여부에 따라서 프론트에서 보이는 버튼을 다르게 설정하였다.

타임리프에 security와 관련해서 사용할 수 있는 문법이 있어 간단하게 구현할 수 있었다.

각 페이지별로 필요한 버튼들을 인증 여부에 따라 설정하였다.

<!--인증하지 않았을때 보이는 화면-->
<div sec:authorize-expr="!isAuthenticated()">
<ul class="nav">
    <li class="nav-item">
        <a class="nav-link" href="/user/login">로그인</a>
    </li>
    <li class="nav-item">
        <a class="nav-link" href="/user/signup">회원가입</a>
    </li>
</ul>
</div>
<!--인증시 보이는 화면-->
<div sec:authorize-expr="isAuthenticated()">
    <a class="btn btn-primary" href="/write" role="button">글쓰기</a>

    <form id="my_form" method="get" action="/user/logout">
        <a id="logout-text" href="javascript:{}" onclick="document.getElementById('my_form').submit();">로그아웃</a>
    </form>
</div>

 

그리고 댓글을 작성자 본인만 수정, 삭제할 수 있도록 구현하였다.

로그인 여부가 아니라 작성자 본인으로 권한을 설정해야해서 어려웠다.

detail 페이지에서 로그인한 작성자의 id를 불러와서 댓글에 저장한 userId와 동일한 경우에만 수정, 삭제 버튼이 보이게 하였다.

html 페이지에서 로그인 정보를 어떻게 확인해야 하는지, 그리고 이 정보를 ajax로 불러온 userId와 어디서 비교를 해야할지 고민이 많았다.

마침 오후에 기술매니저님이 방문하셔서 조언을 구했는데, 로그인 후 서버가 가지고 있는 로그인 정보를 프론트에서 ajax로 요청해 받아오는 방식을 방향을 정하게 되었다.

 

thymeleaf 문법으로도 로그인한 사용자 정보를 불러올 수 있는데

1) 이 정보를 변수로 설정하는 방법을 찾지 못했고(처음에 view와 함께 model로 보내려고 했다)

2) 해당 기능을 위해 페이지를 thymeleaf로 변경하고 싶지는 않았다.

페이지 로딩만 thymeleaf로 구현하고 대부분의 데이터는 ajax로 불러오게 설계하였기 때문에 해당 페이지만 구현 방식을 바꾸고 싶지 않았다.

 

ajax로 불러오는 정보들 중에 같이 불러올 수 있으면 기존 컨트롤러에 포함시키려고 했는데 마땅치 않아서 별도로 컨트롤러+ajax를 작성하였다.

처음에 제대로 동작하지 않아서 살펴보니, ajax로 loginId가 서버에서는 잘 왔는데 이 값을 다른 function에서 사용할 때는 값이 지정되지 않았다.

ajax가 기본적으로 비동기방식이라 발생한 문제로, 동기방식으로 사용하기 위한 설정을 추가하고 ajax 전에 먼저 변수를 선언하여 전역변수로 설정하였다.

//loginId받아와 전역변수로 설정하기
function getLoginInfo() {
    var loginId;
    $.ajax({
        type: "GET",
        url: "/logininfo",
        async: false,
        success: function (response) {
           loginId = response;
        }
    })
    return loginId;
}
let loginId = getLoginInfo();
if (loginId != userId) {
    $('#updatebutton').hide();
    $('#deletebutton').hide();

그리고 회원가입의 유효성 검사를 일부 구현했다.

다음과 같이 validation을 사용해서 서버 자체에서 유효성을 검사할 수 있다.

먼저 gradle dependencies를 추가한다.

implementation 'org.springframework.boot:spring-boot-starter-validation'
@NotBlank(message = "username을 입력해주세요.")
@Pattern(regexp = "^[a-zA-Z0-9]{3,10}$", message = "username은 영어 대/소문자와 숫자로 3~10자로 입력해주세요.")
private String username;

//동작 제대로 안함
@Min(value = 4, message = "password는 4자 이상 입력해주세요.")
private String password;

@NotBlank(message = "이메일을 입력해주세요.")
@Email(message = "올바른 이메일 주소를 입력해주세요.")
private String email;

그리고 이렇게 회원가입 요청 처리 controller와 service에 관련 메소드를 추가해서 에러 메시지를 model로 보내고 타임리프 문법으로 프론트에서 보여줄 수 있다.

에러의 경우 작성하던 값을 유지할 수 있도록 requestDto도 보내주었는데 프론에서 제대로 동작하지 않았다.

그리고 password값은 유효성 검사가 제대로 동작하지 않는다(아마 암호화해서 값이 들어와서 인 것 같다)

프론트에서 form-action으로 구현된 그대로 유효성 검사 함수를 추가하니 잘 작동하지 않아서 아에 회원가입 페이지를 좀 수정하는 방식으로 변경할까 싶다. 

@PostMapping("/user/signup")
public String registerUser(@Valid SignupRequestDto requestDto, Errors errors, Model model) {
    if (errors.hasErrors()) {
        model.addAttribute("requestDto", requestDto);

        Map<String, String> validatorResult = userService.validateHandling(errors);
        for (String key : validatorResult.keySet()) {
            model.addAttribute(key, validatorResult.get(key));
        }
        return "/signup";
    }
    userService.registerUser(requestDto);
    return "redirect:/user/login";
}

우선 내일은 테스트 코드 부분을 먼저 진행하고,

잘못된 경로에 대한 부분(AuthenticationEntryPoint를 사용할지, 아니면 해당 페이지에서 로그인 여부에 따라서 경고창을 띄우는 방식으로 해야할지는 잘 모르겠다.)

회원가입 부분 마무리 하면 될 것 같다.

 

이번 주 개인과제는 구현해야할 기능도 많고, 검색해서 해결하는 것도 잘 안되서 정말 쉽지 않은 것 같다.

약간 계속 과부하 상태에 있는 느낌?? spring security도 더 공부해야할 것 같다.