BlueNyang
[멀티캠퍼스] 풀스택 개발자 아카데미 (24) - Spring Framework(4)
Java 풀스택 아카데미
· 멀티캠퍼스 JAVA 풀스택 개발자 아카데미 6회차 (24편)

[멀티캠퍼스] 풀스택 개발자 아카데미 (24) - Spring Framework(4)

BlueNyangBlueNyang
·
·
약 6분
·
# 부트캠프후기# 멀티캠퍼스it부트캠프# [현대이지웰] JAVA 풀스택 개발자 아카데미 6회차# restful-api# ajax# asynchronous# lombok
시리즈·멀티캠퍼스 JAVA 풀스택 개발자 아카데미 6회차(30개의 글)
  • ···
  • 23.[멀티캠퍼스] 풀스택 개발자 아카데미 (23) - Spring Framework(3)
  • 24.[멀티캠퍼스] 풀스택 개발자 아카데미 (24) - Spring Framework(4)현재
  • 25.[멀티캠퍼스] 풀스택 개발자 아카데미 (25) - Spring Boot(1)
  • ···

1. 정적인 화면을 넘어 동적인 웹으로

지난 글에서는 Spring MVC 환경에 MyBatis를 연동하여, JDBC의 반복적인 작업을 줄이고 SQL을 XML로 분리하는 효율적인 데이터 접근 방식을 구축했다. 컨트롤러, 서비스, 매퍼로 이어지는 계층형 구조를 통해 애플리케이션의 역할과 책임을 명확히 나누는 방법도 익혔다.

하지만 지금까지 우리가 만든 웹 애플리케이션은 사용자가 링크를 클릭하거나 폼을 제출할 때마다 서버로부터 새로운 HTML 페이지 전체를 다시 받아와야 하는 '전체 페이지 새로고침' 방식으로 동작했다. 이는 사용자 경험(UX)을 저해하고 서버에 불필요한 부담을 준다.

이번 글에서는 이러한 정적인 방식을 벗어나, 화면 전환 없이 서버와 데이터를 주고받는 비동기(Asynchronous) 통신의 핵심 기술을 알아본다. 데이터 중심의 통신 규약인 REST의 개념을 이해하고, Spring에서 @ResponseBody@RestController를 사용해 RESTful API를 구현하는 방법을 살펴본다. 또한, jQuery의 Ajax를 사용하여 페이지 새로고침 없이 ID 중복 체크 기능을 구현하는 실습을 진행한다. 마지막으로, 반복적인 VO/DTO 코드를 획기적으로 줄여주는 Lombok 라이브러리의 도입 방법과 활용법도 함께 다룬다.


2. REST란 무엇인가? 뷰(View)가 아닌 데이터(Data)를 전송하다

전통적인 웹 애플리케이션은 서버가 데이터(Model)와 HTML(View)을 결합하여 완성된 페이지를 클라이언트(브라우저)에게 전송했다. 그러나 스마트폰과 같은 모바일 기기가 대중화되면서, 동일한 서버 데이터가 웹(HTML)뿐만 아니라 모바일 앱(iOS, Android)에도 전송되어야 할 필요성이 생겼다.

모바일 앱은 HTML이 필요 없고, 오직 순수한 데이터(예: JSON, XML)만 있으면 된다. REST(Representational State Transfer) 는 이러한 요구에 부응하는 아키텍처 스타일로, 웹의 장점을 활용하여 자원(Resource)을 이름(URI)으로 구분하고, 해당 자원의 상태(Representation)를 주고받는 방식을 의미한다.

  • 전통 방식: GET /productList.do → 서버가 productList.jsp를 렌더링한 HTML 페이지를 반환
  • REST 방식: GET /api/products → 서버가 상품 목록 JSON 데이터를 반환
전통적인-웹-방식과-REST-API-통신-방식-비교
전통적인-웹-방식과-REST-API-통신-방식-비교

Spring MVC에서는 이러한 REST 방식의 응답, 즉 뷰 리졸버(ViewResolver)를 거치지 않고 데이터 자체를 HTTP 응답 본문(Body)에 직접 써넣는 기능을 제공한다.


3. Spring에서의 RESTful API 구현

Spring에서 컨트롤러 메소드가 뷰 이름이 아닌 데이터 자체를 반환하도록 만드는 핵심 어노테이션은 @ResponseBody@RestController이다.

3.1. ResponseBody - 메소드 단위로 REST 적용

@ResponseBody 어노테이션은 메소드 레벨에 적용되어, 해당 메소드의 반환값(String, VO, List 등)이 뷰 이름으로 해석되지 않고 HTTP 응답 본문에 직접 작성되도록 지시한다.

이때 Spring은 jackson-databind와 같은 라이브러리(MessageConverter)를 사용하여 반환된 자바 객체를 JSON 형식의 문자열로 자동 변환해준다. (pom.xmljackson-databind 의존성을 추가해야 한다.)

java
@Controller
public class ProductController {

    // ...

	@ResponseBody
    @RequestMapping(value = "/product/checkId", method = RequestMethod.GET)
    public String checkId(@RequestParam("id") String id) {
        boolean isDuplicate = productService.checkId(id);
        if (isDuplicate) {
            return "DUPLICATE";
        } else {
            return "OK";
        }
    }
}
ResponseBody-어노테이션-동작-흐름도
ResponseBody-어노테이션-동작-흐름도

3.2. RestController - 클래스 단위로 REST 적용하기

만약 클래스의 모든 메소드가 뷰를 반환하지 않고 오직 데이터(REST API)만 반환하도록 설계된다면, @Controller 대신 @RestController 어노테이션을 사용하는 것이 편리하다.

@RestController@Controller@ResponseBody를 합쳐놓은 어노테이션이다. 이 어노테이션이 붙은 클래스의 모든 메소드에는 @ResponseBody가 자동으로 적용된다.

java
@RestController // 이 클래스의 모든 메소드는 @ResponseBody가 적용
@RequestMapping("/api/products") // API 공통 경로
public class ProductRestController {

    @Autowired
    private ProductService productService;

    @RequestMapping(value = "/{prdNo}", method = RequestMethod.GET)
    public ProductVO getProduct(@PathVariable("prdNo") String prdNo) {
        // ProductVO 객체가 JSON 형식으로 자동 변환되어 응답됨
        return productService.detailViewProduct(prdNo);
    }

    @RequestMapping(value = "", method = RequestMethod.GET)
    public List<ProductVO> getAllProducts() {
        // List<ProductVO> 객체가 JSON 배열 형식으로 자동 변환됨
        return productService.listAllProduct();
    }
}

4. Ajax

Ajax(Asynchronous JavaScript and XML) 는 JavaScript를 사용하여 브라우저가 서버에게 비동기적으로 요청을 보내고 응답을 받을 수 있게 하는 기술이다. '비동기적'이라는 말은, 서버와 통신하는 동안 사용자는 다른 작업을 계속할 수 있으며 페이지 전체가 멈추거나 새로고침되지 않는다는 의미이다.

동기-방식과-비동기-Ajax-방식-비교
동기-방식과-비동기-Ajax-방식-비교

우리는 jQuery 라이브러리가 제공하는 $.ajax() 함수를 사용하여 Ajax 통신을 간편하게 구현할 수 있다.

$.ajax()의 주요 옵션:

  • url: 요청을 보낼 서버의 URL
  • type: HTTP 메소드 (예: "GET", "POST")
  • data: 서버로 전송할 데이터 (예: { id: "test", pw: "1234" })
  • dataType: 서버로부터 받을 데이터의 예상 형식 (예: "json", "text", "html")
  • success: 요청이 성공했을 때 실행될 콜백 함수. 서버가 보낸 응답 데이터가 파라미터로 전달된다.
  • error: 요청이 실패했을 때 실행될 콜백 함수

5. Ajax를 이용한 상품번호 중복 체크

MyBatis 연동 실습에서 만든 상품 등록 폼에 '상품번호 중복 체크' 기능을 Ajax를 이용해 구현해볼 수 있다.

5.1. View 수정 (newProductForm.jsp)

상품번호 입력란 옆에 중복 확인 버튼과 결과 메시지를 표시할 영역을 추가한다. 또한 jQuery 라이브러리와 우리가 작성할 JS 파일을 포함시킨다.

html
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="${pageContext.request.contextPath}/resources/js/prdNoCheck.js"></script>

<tr>
  <td>상품번호</td>
  <td>
    <input type="text" name="prdNo" id="prdNo" />
    <button type="button" id="prdNoCheckBtn">중복 확인</button>
    <br />
    <span id="checkResult"></span>
  </td>
</tr>
상품-등록-폼에-중복-확인-버튼과-메시지-영역-추가
상품-등록-폼에-중복-확인-버튼과-메시지-영역-추가

5.2. JavaScript 작성 (/resources/js/prdNoCheck.js)

src/main/webapp/resources 디렉터리(STS 3.9 기준)에 js 폴더를 만들고 prdNoCheck.js 파일을 생성한다. (servlet-context.xml<resources mapping="/resources/**" ... /> 설정이 되어 있어야 한다.)

js
$(function () {
  $("#prdNoCheckBtn").on("click", function () {
    const prdNo = $("#prdNo").val(); // 입력된 상품번호
    const contextPath = "/mybatis"; // 프로젝트 Context Path (환경에 맞게 수정)

    // 상품번호를 입력하지 않은 경우
    if (!prdNo) {
      $("#checkResult").text("상품번호를 입력하세요.").css("color", "red");
      return;
    }

    $.ajax({
      url: contextPath + "/product/prdNoCheck", // 요청 URL
      type: "POST", // POST 방식
      data: { prdNo: prdNo }, // 서버로 보낼 데이터
      dataType: "text", // 서버로부터 받을 데이터 형식
      success: function (result) {
        // 서버로부터 받은 응답(result)에 따라 메시지 표시
        if (result.trim() === "AVAILABLE") {
          $("#checkResult").text("사용 가능한 상품번호입니다.").css("color", "green");
        } else {
          $("#checkResult").text("사용할 수 없는 상품번호입니다.").css("color", "red");
        }
      },
      error: function (xhr, status, error) {
        // 통신 실패 시
        $("#checkResult")
          .text("오류 발생: " + error)
          .css("color", "red");
      },
    }); // end ajax
  }); // end click event
}); // end ready

5.3. Controller 리팩토링

Ajax 요청을 처리할 메소드를 ProductController에 추가한다. 이 메소드는 뷰가 아닌 텍스트(String)를 반환해야 하므로 @ResponseBody를 반드시 붙여야 한다.

java
// ProductController.java

// ... (기존 메소드들) ...

// Ajax 요청 처리: 상품번호 중복 체크
@ResponseBody // Ajax 요청이므로 뷰가 아닌 데이터 반환
@RequestMapping(value = "/product/prdNoCheck", method = RequestMethod.POST)
public String prdNoCheck(@RequestParam("prdNo") String prdNo) {
    String resultMessage = "";
    String foundId = productService.prdNoCheck(prdNo);

    if (foundId == null) {
        resultMessage = "AVAILABLE";
    } else {
        resultMessage = "DUPLICATE";
    }

    return resultMessage;
}
컨트롤러에서-Ajax-요청-처리-및-데이터-반환
컨트롤러에서-Ajax-요청-처리-및-데이터-반환

5.4. Service 및 Mapper 수정

ProductServiceProductMapperprdNoCheck 메소드를 구현(이전 글 참고)

  • ProductService.java: public String prdNoCheck(String prdNo) { return productMapper.prdNoCheck(prdNo); }
  • ProductMapper.xml: <select id="prdNoCheck" ...> 쿼리

이제 서버를 실행하고 상품 등록 폼에서 상품번호를 입력한 뒤 '중복 확인' 버튼을 누르면, 페이지 새로고침 없이 비동기적으로 중복 여부 결과가 span#checkResult 영역에 표시되는 것을 확인할 수 있다.

Ajax-ID-중복-체크-실행-결과-화면1
Ajax-ID-중복-체크-실행-결과-화면1
Ajax-ID-중복-체크-실행-결과-화면2
Ajax-ID-중복-체크-실행-결과-화면2

6. Lombok으로 반복 코드 줄이기

우리가 작성한 ProductVO와 같은 모델 클래스들을 살펴보자. 데이터를 담기 위한 필드를 선언하고, 이 필드에 접근하기 위한 Getter, Setter 메소드, toString(), equals(), hashCode(), 생성자 등을 반복적으로 작성해야 한다. 이는 매우 지루하고 코드 라인 수만 늘리는 작업이다.

Lombok은 이러한 반복적인 자바 코드를 어노테이션 기반으로 컴파일 시점에 자동으로 생성해주는 라이브러리이다.

6.1. Lombok 설치 (pom.xml 및 IDE 플러그인)

1. pom.xml 의존성 추가:

XML

xml
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
    <scope>provided</scope>
</dependency>
  1. IDE 플러그인 설치:

Lombok은 IDE가 어노테이션을 인지하고 컴파일 에러를 표시하지 않도록 반드시 IDE용 플러그인을 설치해야 한다. STS 3.9(Eclipse)나 IntelliJ IDEA 모두 마켓플레이스(Marketplace)에서 'Lombok'을 검색하여 설치할 수 있다.

IntelliJ-IDEA-Lombok-플러그인-설치-화면
IntelliJ-IDEA-Lombok-플러그인-설치-화면

6.2. VO 클래스에 Lombok 적용하기

Lombok을 적용하면 ProductVO.java 파일이 다음과 같이 간결해진다.

Before (Lombok 미적용):

java
public class ProductVO {
    private String prdNo;
    private String prdName;
    // ...
    private Date prdDate;

    // 기본 생성자
    public ProductVO() { }

    // Getter/Setter 수십 줄
    public String getPrdNo() { return prdNo; }
    public void setPrdNo(String prdNo) { this.prdNo = prdNo; }
    // ... (모든 필드에 대해 반복) ...

    // toString()
    @Override
    public String toString() {
        return "ProductVO{" + ... + "}";
    }
}

After (Lombok 적용):

java
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.util.Date;

@Data // @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor를 합친 어노테이션
@NoArgsConstructor // 파라미터가 없는 기본 생성자
@AllArgsConstructor // 모든 필드를 파라미터로 갖는 생성자
public class ProductVO {
    private String prdNo;
    private String prdName;
    private int prdPrice;
    private String prdCompany;
    private int prdStock;
    private Date prdDate;
}

이제 @Data 어노테이션 하나로 수십 줄의 코드가 사라졌다. 개발자는 비즈니스 로직에 더 중요한 필드를 정의하는 데만 집중할 수 있으며, 가독성과 생산성이 크게 향상된다.

Lombok은 DI(의존성 주입)에서도 사용하기 좋다.

Before:

java
@RestController
public class ProductController {
	@Autowired
	private ProductServce productService;
	@Autowired
	private CategoryService categoryService;

	// ... 컨트롤러 로직
}

After:

java
@RestController
@RequiredArgsConstructor // 이 Annotation을 통해 private final 필드에 의존성이 주입된다.
public class ProductController {
	private final ProductServce productService;
	private final CategoryService categoryService;

	// ... 컨트롤러 로직
}

7. 결론 및 평가

이번 글에서는 Spring MVC를 사용하여 뷰(HTML)가 아닌 데이터(JSON/Text)를 반환하는 RESTful 방식의 통신 방법을 알아보았다. @ResponseBody@RestController는 이러한 API를 구현하는 핵심 어노테이션임을 확인했다. 또한, jQuery Ajax를 활용하여 페이지 새로고침 없이 서버와 통신하는 'ID 중복 체크' 기능을 구현함으로써 사용자 경험을 향상시키는 방법을 실습했다. 마지막으로 Lombok을 도입하여 VO/DTO 클래스의 반복적인 코드를 제거하고 생산성을 높이는 팁까지 살펴보았다.

Ajax와 RESTful API는 현대 웹 애플리케이션(특히 SPA, Single Page Application)을 구축하는 데 필수적인 기술이다. 오늘 실습한 'ID 중복 체크'는 Ajax의 가장 고전적이고 실용적인 예시로, 비동기 통신의 이점을 명확하게 보여준다. Lombok은 선택 사항이지만, 한번 경험하면 빠져나오기 힘든 강력한 생산성 도구이다.

지금까지 우리는 STS 3.9 환경에서 Spring의 핵심인 IoC/DI, Spring MVC의 DispatcherServlet 구조, MyBatis 연동, 그리고 Ajax/REST 통신까지 엔터프라이즈 웹 개발의 전통적인 기반을 모두 다루었다.

하지만 web.xml, servlet-context.xml, root-context.xml 등 복잡한 XML 설정은 여전히 개발자에게 부담으로 남아있다. 다음 글에서 다룰 Spring Boot는 이러한 XML 설정의 시대를 끝내고, 자동 설정(Auto-Configuration)내장 WAS를 기반으로 빠르고 간편하게 독립적으로 실행 가능한 애플리케이션을 만드는 스프링 부트(Spring Boot) 에 대해 다뤄볼 예정이다.

BlueNyang
작성자BlueNyang
라이선스
CC BY NC
BlueNyang

BlueNyang

BlueNyang의 개발 log

카테고리

  • Development
  • Framework
  • Language
  • Dev Tools
  • DevOps & Infra
  • Studies

페이지

© 2026 BlueNyang. All rights reserved.

Made with Nuxt.js and Directus