본문 바로가기
개발자 전향 프로젝트

자주 쓰이는 Ajax 활용법 - 부분 갱신 feat.@ResponseBody

by 샘오리 2022. 11. 21.
728x90
반응형

클라이언트는 서버에 다양한 요청을 날리게 되고

서버는 그러한 요청이 올바른 요청인지 확인을 거치고 응답을 하게 된다.

이러한 요청과 응답이 세트로 일어날 때 크게 두가지 방식으로 진행되는데 

하나는 동기이고 하나는 비동기이다.

 

동기는 화면이 갱신된다는 특징이 있고

비동기는 화면 갱신 없이, 즉, 새로고침없이 응답을 페이지에 노출시킬 수 있다는 특징이 있다.

그리고 새로고침없이 응답을 view에 뿌려주는 작업을 해주는 대표적인 자바스크립트 라이브러리가 Ajax이다.

 

Ajax 는 Asynchronous Javascript And Xml의 약자이다.

튤립국 프로팀 아약스 아니다

JavaScript를 사용한 비동기 통신, 클라이언트와 서버간에 XML 데이터를 주고받는 기술인 셈이다.

 

이 Ajax를 사용하는 이유는 단순히 view단에 뿌려줄 때 화면 갱신이 없어서 그런 것은 아니다.

기본적으로 비동기라는 것은 아래와 같은 장점이 있다.

  • 필요한 부분만 불러와 사용할 수 있어 불 필요한 리소스 낭비를 줄일 수 있다. 

아무튼 오늘은 이 Ajax를 이용하여 DB에 접속 및 데이터를 추가/수정하는 방법을 알아보고

추가된/변경된 데이터를 새로고침(화면 깜빡임) 없이 부분 갱신하는 방법을 알아볼 것이다.

 

MyBatis, Thymeleaf 기준


아래는 샘플 Ajax 형식이다.

            $.ajax({
            	// 추가 or 수정이기에 POST
                type: 'POST',
                url: '/컨트롤러 매핑 경로',
                data: 보낼 데이터 변수,
                success: function (data) {
                    //Ajax 통신이 성공했을 때 쓰고 싶은 로직은 여기에
                    
                }
            });

 

우리는 여기서 ajax 요청이 제대로 통신만 됐다면 실행시킬 로직에 조건문을 달아줄 것이다.

그 조건은 data가 1일 경우이며 임의로 data가 1일 경우에만 로직을 실행하도록 만드는 것이다.

success: function (data) {
    if(data === 1){
        // Controller에서 응답한 값이 1이라면, 1번 조건일 경우, 실행시키는 로직

    }
}

이렇게 되면 컨트롤러에서 한번 검증을 거치고 나서 원하는 로직이 올바르게 실행됐을 경우에만 1이라는 숫자를 응답하도록 할 수 있고 자바스크립트 라이브러리인 Ajax에서 추가 로직을 진행할 수 있는 것이다.

 

아래는 샘플 컨트롤러이다.

@ResponseBody
@PostMapping("/경로")
public Integer 함수명(@ModelAttribute Dto클래스명 Dto참조변수) {

        // 서비스의 함수를 호출했을 때 리턴값이 1번인 경우 1을 응답
        if(서비스 참조변수명.서비스 함수명(Dto참조변수) == 1){
            return 1;
        // 서비스의 함수를 호출했을 때 리턴값이 2번인 경우 2를 응답    
    	}else if(서비스 참조변수명.서비스 함수명(Dto참조변수) == 2){
            return 2;
    	}else{
            return null;
        }
    }

 

기본적으로 Controller는 템플릿 엔진에 기반하여 resources 폴더 밑 view 파일을 띄우는 게 일반적이다.

하지만 view를 띄우는 것이 아니라 요청을 한 바로 그 곳으로 특정 데이터 타입의 값을 그대로 응답하길 바랄 때가 있을 것이다. 특정 view 를 다시 띄우는 것이 아니라 부분갱신을 하고 싶다거나, 자바스크립트 ajax에서 다른 view로 redirect 하고 싶을 때 유용할 것이다.

 

아무튼 @ResponseBody라는 애노테이션을 통해 원하는 값만 응답할 수 있다.

참고로 이는 @Controller 기준이며
@RestController에서는 기본적으로 @ResponseBody가 붙어있기 때문에 해당 애노테이션이 불필요하다.

아래는 샘플 서비스 혹은 서비스 impl ( a.k.a implementation)이다.

CRUD 중 Update를 하는 것을 샘플로 지정했다. 

public Integer 함수명(Dto클래스명 Dto참조변수) {
     if(Dto참조변수.get칼럼명==조건 1번) {
         sqlSession.update("매퍼", Dto참조변수);
         return 1;
     }else if((Dto참조변수.get칼럼명==조건 2번){
         sqlSession.update("매퍼", Dto참조변수);
         return 2;
     }else{
         return null;
}

 

  1. 서비스에서 매퍼를 호출하고
  2. 매퍼에 있는 쿼리를 받아 DB에 연결하고
  3. DB에서 원하는 동작을 수행하고
  4. 조건에 따라 각각 다른 정수를 return 하게 했으니,
  5. return 되는 정수는 자신을 호출한 컨트롤러로 돌아 가게 되고
  6. 컨트롤러는 응답받은 정수에 따라 다시 호출한 Ajax에 조건에 부합하는 정수를 return 한다.

 

그래서 궁극적으로 1번 조건에 부합한다는 가정하에 1이라는 숫자가 return 될 것이고

1이 응답됐다면 아래 코드가 동작하게 될 것이다.

success: function (data) {
    if(data === 1){
        // Controller에서 응답한 값이 1이라면, 1번 조건일 경우, 실행시키는 로직

    }
}

이제 추가든 수정이든 DB에 데이터를 추가/조작하는 로직을 성공적으로 실행했고 페이지에서 비동기적으로

새로고침 혹은 리로드 없이 부분적으로 갱신하는 일만 남았다.

 

그 방법은 놀랍게도 Ajax 안에 Ajax를 넣는 것이다.

말 그대로 처음 추가 혹은 수정 작업을 할 때의 Ajax는 그저 DB에 침투하여 원하는 작업을 하는 것이었고

원하는 작업이 성공적이었다면? 이번에는 변경된 데이터를 긁어오는 작업을 하는 것이다.

 

아래는 샘플 Ajax 코드이다.

if (data === 1) {
    $.ajax({
        type: 'GET',
        url: '/경로',
        success: function (data) {
            $('#부분 갱신하고 싶은 DIV의 ID').replaceWith(data);
        }
    });
}

이제 컨트롤러를 보자. 

    @GetMapping("/경로")
    public String 함수명(Model model) {
            model.addAttribute("담을 변수", 서비스 참조변수.변경된 데이터를 가져오는 서비스 함수);
            return "/HTML 이름 :: #부분 갱신하고 싶은 DIV의 ID";
    }

두번째 Ajax에서 Get으로 해당 경로에 호출을 했고 해당 컨트롤러에서 변경된 데이터를 긁어오는 함수를 호출했으니

그 함수가 올바르게 작동했다면 변경된 데이터를 응답받아

key:value로 동작하는 model.addAttribute라는 메소드를 통해

지정한 변수(key)에 가져온 데이터(value)를 삽입하고

 

그 변수를 return하는 HTML 의 선택된 부분으로 쏴주게 되고

그 부분에서 데이터를 모두 담은 변수를 반복문을 통해 순차적으로 풀어주면?

HTML 에 선택된 부분만 새로고침 없이 갱신되는 것이다.

 

눈여겨볼 것은 HTML 이름 다음 :: 인데 이렇게 return 할 경우 해당 html의 특정 부분으로만 return 되는 것을 알 수 있고 Ajax에서 그 data를 받아와서 추가적으로 그 데이터를 아래처럼 특정 DIV에 갈아끼우는 것이다.

$('#부분 갱신하고 싶은 DIV의 ID').replaceWith(data);

 

아래는 기본적인 부트스트랩 테이블을 기준으로 변경된 데이터가 담긴 변수를 어떻게 뿌려주는지와

부분 갱신하고 싶은 곳의 ID는 어떻게 되어있는지 나와있는 HTML 샘플 코드이다.  

 

<div class="card-box table-responsive" id="부분 갱신하고 싶은 곳의 ID">
    <table class="table table-hover">
        <thead>
        <tr class="table-row">
            <th class="table-head">1</th>
            <th class="table-head">2</th>
            <th class="table-head">3</th>
        </tr>
        </thead>
        <tbody class="table-content">
        <tr th:each="반복 변수(상태변수) : ${컨트롤러에서 응답받은 데이터를 담은 변수}">
            <td th:text="${반복 변수.칼럼명}"></td>
            <td th:text="${반복 변수.칼럼명}"></td>
            <td th:text="${반복 변수.칼럼명}"></td>
        </tr>
        </tbody>
    </table>
</div>

위 코드에 나온 것 처럼 컨트롤러에서 응답받은 값을 한 테이블을 크게 묶어주는 DIV의 ID에 연결시켰고

//컨트롤러
return "/HTML 이름 :: #부분 갱신하고 싶은 DIV의 ID";

//HTML 안의 DIV ID
<div class="card-box table-responsive" id="부분 갱신하고 싶은 DIV의 ID">

그 응답받은 값을 모아서 담은 변수를 table row를 나타내는 tr에서 th:each 라는 반복문을 통해 반복 변수에 담아주었다.

//컨트롤러
model.addAttribute("담을 변수", 서비스 참조변수.변경된 데이터를 가져오는 서비스 함수);

// HTML 안의 TABLE 안의 TR
<tr th:each="반복 변수(상태변수) : ${컨트롤러에서 응답받은 데이터를 담은 변수}">

이후 table data를 나타내는 td에 반복 변수.칼럼명으로 순차적으로 표시하는 것을 볼 수 있다. 

 

아래는 예시이다.

        <tr th:each="product : ${productList}">
            <td th:text="${product.name}"></td>
            <td th:text="${product.price}"></td>
            <td th:text="${product.size}"></td>​

728x90
반응형