Ajax를 이용한 관심주제(Tag) 등록

김건우·2023년 2월 1일
0

비동기 Axios

목록 보기
2/3
post-thumbnail

현재 구현하고 있는 프로젝트는 회원의 관심주제라는 서비스가 필요하다. 따라서 회원(Account)와 관심주제(Tag) 는 연관관계를 맺고 있다. 그리고 관심주제에 대한 등록 ,삭제는 tagify를 사용해서 구현하였다.

NPM 설치

npm install @yaireo/tagify

위와 같이 CMD창에 ls 를 치면 현재 경로에 package.json이 존재하는 위치에 위의 명령어로 tagify를 다운 받아주자.

관심주제 등록 Input창

 <input id="tags" type="text" name="tags" th:value="${#strings.listJoin(tags,',')}"
    class="tagify-outside" aria-describedby="tagHelp"/>

tagify라이브러리를 사용한 Ajax

 <script type="application/javascript" th:inline="javascript">
        $(function() {
            function tagRequest(url, tagTitle) {
                $.ajax({
                    dataType: "json",
                    autocomplete: {
                        enabled: true,
                        rightKey: true,
                    },
                    contentType: "application/json; charset=utf-8",
                    method: "POST",
                    url: "/settings/tags" + url,
                    data: JSON.stringify({'tagTitle': tagTitle})
                }).done(function (data, status) {
                    console.log("${data} and status is ${status}");
                });
            }

            function onAdd(e) {
                tagRequest("/add", e.detail.data.value);
            }


            var tagInput = document.querySelector("#tags");
            var tagify = new Tagify(tagInput, {
                pattern: /^.{0,20}$/,
                dropdown : {
                    enabled: 1,
                }
            });
            tagify.on("add", onAdd);
        
            // add a class to Tagify's input element
            tagify.DOM.input.classList.add('form-control');
            // re-place Tagify's input element outside of the  element (tagify.DOM.scope), just before it
            tagify.DOM.scope.parentNode.insertBefore(tagify.DOM.input, tagify.DOM.scope);
        });
    </script>

우선 등록을 Ajax를 통해 Post방식의 url은 "/settings/tags/add"로 요청하도록 코드를 짰다. 하지만 여기서 문제가 발생했다. 바로 시큐리티 적용으로 인해 CSRF Token을 추가 적으로 설정해주어야했다.

CSRF Token이란?

CSRF Token은 임의의 난수를 생성하고 세션에 저장한다.
그리고 사용자의 매 요청마다 해당 난수 값을 포함시켜서 전송시킨다.
이후 백엔드에서는 요청을 받을 때 마다 세션에 저장된 토큰값과 요청 파라미터에 전달된 토큰 값이 같은지 검사한다.
스프링 시큐리티에서는 공식적으로 이 CSRF 공격에 대한 방어 기능을 시큐리티 모듈에서 제공해주고 있다.

외부 자바스크립트 jquery ajax 통신 csrf token 설정하기

  <script type="application/javascript" th:inline="javascript" th:fragment="ajax-csrf-header">
        $(function() {
            var csrfToken = /*[[${_csrf.token}]]*/ null;
            var csrfHeader = /*[[${_csrf.headerName}]]*/ null;
            $(document).ajaxSend(function (e, xhr, options) {
                xhr.setRequestHeader(csrfHeader, csrfToken);
            });
        });
    </script>

관심주제 등록 Controller

HTTPmessage Body로 들어오는 요청값을 받기위해 @RequestBody TagForm tagForm로 요청을 받는다

    @PostMapping("/settings/tags/add")
    @ResponseBody
    public ResponseEntity addTags(@CurrentUser Account account, @RequestBody TagForm tagForm) {
        log.info("에이작스 요청 :{}",tagForm);
        //태그 제목이 없을 시에 save
        Tag tag = tagService.findOrCreateNew(tagForm.getTagTitle());
        accountService.addTag(account, tag);//연관관계 편의 메서드
        return ResponseEntity.ok().build();
    }

아래의 메서드는 tagService단에서 이미 관심주제를 등록했는지 검사하는 메서드이다. 존재한다면 조회 후 반환하고 존재하지 않는다면 save를 한다.

  • tagService.findOrCreateNew(tagForm.getTagTitle());
@Service
@Transactional
@RequiredArgsConstructor
public class TagService {

    private final TagRepository tagRepository;

    public Tag findOrCreateNew(String tagTitle) {
        Tag tag = tagRepository.findByTitle(tagTitle);
        if (tag == null) {
            tag = tagRepository.save(Tag.builder().title(tagTitle).build());
        }
        return tag;
    }
}
  • 등록 후 저장된 관심주제

    strings.listJoin

    문자열 리스트를 ' , ' 를 사용해서 변환해준다.
    관심주제를 문자열 List로 MODEL에서 넘겨주기 때문에 ${#strings.listJoin(tags,',')}를 사용해서 등록 후 페이지에 조회하게 구현했다.

    EX) List tags = List.of("Spring", "JPA");
    => Spring, JPA

    <input id="tags" type="text" name="tags" th:value="${#strings.listJoin(tags,',')}" class="tagify-outside" aria-describedby="tagHelp"/>
    • strings.listJoin() 문법을 사용한 페이지🔽
profile
Live the moment for the moment.

0개의 댓글