Spring

[Spring + Javascript] fetch()를 활용한 api방식 웹페이지 기초 (3)

jongh0 2025. 6. 19. 20:01

해당 게시물에서는 게시판 CRUD 작업과 게시판에 각각의 게시물 페이지 포워딩 방식에 대해서 설명하려고 한다.

 

[클래스 구성]

BoardApiController - 자바스크립트에 fetch() 함수 요청에 대한 CRUD 작업 및 응답을 담당

BoardController - 단순하게 페이지 포워딩을 위한 컨트롤러

BoardService - Mapper에서 다룰 mybatis sql 구문 들의 트랜잭션 처리를 담당

BoardMapper - mybatis를 활용한 sql 구문 실행

BoardVo - DB 테이블에 담긴 컬럼형식을 필드로 담고있고, lombok을 통해 캡슐화가 진행될 클래스

 

BoardApiController 클래스의 어노테이션

 

 해당 클래스에서는 @RestController 어노테이션으로 @Controller + @ResponseBody 기능을 합친 어노테이션이다.

의존성 주입(DI) 에 있어서 Controller임을 명시하고, ResponseBody는 브라우저로 부터 요청이 왔을 때, 응답을 페이지 포워딩, 리다이렉트 방식이 아닌 데이터를 JSON 형식으로 반환하는 어노테이션이다.

 

@RequestMapping을 통해서 해당 클래스 내에서 사용되는 Mapping 어노테이션에 대해 api/board 키워드를 묶어주고,

@RequiredArgConstructor를 통해서 Service 클래스와의 의존성 주입 방식에 사용되는 생성자를 자동으로 실행시켜준다.

 

경로변수를 통한 Get방식의 요청을 처리하는 함수

 

 

 

 

이전에 사용했던 @GetMapping 어노테이션과 다른방식으로 처리가 가능하다.

위에는 경로변수(@PathVariable 어노테이션)과 @GetMapping에 {}기호를 사용하게 되는데 다음과 같이 처리가 가능해진다.

 

기존에는 http://127.0.0.1:8080/api/board로 요청을 보낼때 어떤 방식(POST GET DELETE PUT 등) 으로 요청이 오는지를 구분했다. 하지만 해당 메서드는 http://127.0.0.1:8080/api/board/??? 과 같이 물음표에 들어가는 키워드를 받아와서 해당 함수를 실행시킬 수 있다. 

 

받아온 x는 글 번호를 받아오게 할 것이므로 controller service mapper를 통해 SELECT 구문으로 WHERE절에 NO = #{x}와 같은 식으로 쿼리문 실행이 가능해진다 . 그리고 받아온 선택한 해당 게시물에 대 한 내용이 담긴 객체를 js로 응답하여

페이지를 구성하게 되는 것이다.

 

BoardService 클래스의 detail() 메서드

사용자가 해당 요청을 보냈다는 것은 특정 페이지를 방문했다는 얘기와 동일하다.

그러므로 게시물 컬럼에 존재하는 조회수 컬럼의 값도 증가를 시켜주어야 한다. 즉, 트랜잭션을 처리하는 Service 클래스에서의

detail 메서드에서 두가지의 쿼리문이 진행되어야 한다.

1. UPDATE 명령문을 통한 조회수 컬럼 값 1 올리기

2. 해당 게시물에 대한 내용을 SELECT 하여 게시물 정보 객체 반환하기

 

BoardMapper 클래스의 mybatis 작업

 

쿼리문에 대해서 작업을 처리만 하면 되는 Mapper 에서는 spring mybaits의 명령문 어노테이션을 통해서 작업을 진행하면 되므로 

추상메서드만 다루게 된다. 그래서 해당 Mapper는 interface로 지정한다.

그리고 쿼리문을 진행하고 결과로는 게시물 정보를 반환한다.

 

 

[게시물 목록에서 클릭이벤트 요청 처리]

 

다시돌아와서 게시물 목록에서 특정 게시물을 클릭했을 때, 특정 게시물의 내용이 담긴 페이지를 보여주기위해 클릭 이벤트 설정이 필요하다. 게시물 목록이 어떤식으로 클릭 이벤트를 처리하는지 확인해보자.

 

list.js 에서 게시물 목록에서 특정 게시물 onclick 달아주기

 

페이지 구성을 확인해보면 BoardApiController에서 처리가 가능한 api/board 키워드가 담긴 url을 통해 fetch함수를 진행한다

 

그러면 Get방식의 해당 키워드를 처리하는 list() 메서드를 통해서 모든 게시물 객체가 담긴 리스트와 결과 메세지가 담긴 Map을 리턴받는다. .then을 통해서 응답받은 resp를 json() 메서드를 통해서 JSON 형태로 반환받은 응답을 Map으로 변환한다. map.voList는 모든 게시물 객체가 담긴 리스트로 해당 리스트를 for of를 통해서 각각의 객체에 대해서 목록을 보여주려는

tb요소에 innerHTML을 통해 목록을 출력한다.

 

특정 게시물 클릭을 통한 상세페이지 포워딩 (+pathVariable)

이때, 글번호 제목  내용 조회수 을 담고있는 tr태그를 클릭했을 때, 상세페이지로 포워딩을 해주기 위해서 onclick 속성을 이용해서

location.href의 값을 /board/detail/${i.no}로 설정하였다. Get방식으로 해당 키워드에 요청을 보내게 되며, for문이 돌면서 각각의 게시물 내용에서 게시물 번호를 가져와서 패스경로를 글번호로 설정한것이다.

이렇게 되면 23번 게시물을 클릭하게 되면, http://127.0.0.1:8080/board/detail/23으로 요청을 보내게 되고 페이지를 포워딩하게 될 것이다.

 

근데 여기서 문제는 board/detail/1, board/detail/2, board/detail/3 이런식으로 각각의 패스경로에 대해서 처리하는 메서드를 Controller 클래스에 모두 각각 만들어주어야 하는 문제가 발생한다. 이때 다음과 같은 방식으로 처리가 가능하다.

 

포워딩 처리를 위한 Controller 클래스

 

board/detail/로 시작하는 모든 요청을 처리

 

 

그래서 일단 어떤 숫자(글번호)가 오던지 상관하지 않고, @GetMapping("detail/*")와 같이 설정하면 board/detail/로 시작하는 요청이 왔을 경우에 일단 모두 board/detail 페이지를 포워딩 해준다. 이렇게 되면 다음과 같이 홈페이지가 출력된다.

글번호가 추가된 url과 포워딩된 detail.jsp

 

아직까지는 데이터를 받아오지 않고 url에 글번호만 추가되었고 비어있는 detail.jsp를 포워딩해주고 있다.

이제는 포워딩해준 detail.jsp에 detail.js를 연결해주고, fetch()를 통해서 아까 만들어둔 BoardApiController에 

detail() 메서드를 통해서 특정 게시물의 데이터를 출력해주기만 하면 된다.

 

 

detail.jsp가 포워딩 되자마자 실행되는 detail.js fetch함수

 

detail.jsp에 연결되어있는 detail.js를 확인해보자. 일단 우리가 게시물 목록에서 클릭한 특정 게시물 번호을 얻을 수 있는 방법은

 

 

아까 url에 추가해준 키워드를 사용하면 된다. 현재 위치해 있는 홈페이지의 url 문자열location.href를 통해서 얻어올 수 있다.

그리고 "/"로 구분이 가능하므로 split() 메서드를 통해서 /를 구분하여 배열로 만들어주고 pop() 메서드를 통해서 맨 마지막 인덱스에 있는 "16"을 꺼낼 수 있다.

 

 

BoardApiController에 api/board/{x} 에 대해서 처리하는 메서드

 

해당 글번호를 통해서 fetch 함수로 127.0.0.1:8080/api/board/16의 url에 GET방식으로 요청을 보내고 글번호 16이  detail 메서드의 패스변수 x에 담기게 되어 16번 게시물 객체를 반환한다. 객체에 담긴 내용을 태그안에 값으로 설정만 해주면 끝이 난다. 

 

16번 게시물 데이터가 담긴 페이지

 

 

다시 정리하자면 , 다음과 같다.

게시물 목록에서 원하는 글 클릭하면 board/detail/??이동 하지만 일단 detail.jsp 포워딩
 jsp 파일에는 js파일이 있으므로 js파일 실행
js에서 location.href.split("/").pop()을 통해서 ?? 값 가져와서 해당 값으로 fetch 함수를 통해
서버에 요청을 보내고 응답받은 ??번 게시물 객체를 통해 게시물 내용을 채움