-
[Java] SpringBoot 프로젝트 : 리스트 페이징 처리(2) - DB 리스트 개수 가져오기(@RequestParam, ModelAndView, xml)프로젝트/기능 정리 2023. 6. 16. 14:34
목적
- 게시판 형식 웹사이트 개발 시 DB에서 글 리스트 불러오기 기능 구현
- 리스트 게시판 페이징 처리 받은 코드 리뷰
지난 글에서는 jsp가 컨트롤러에 어떤식으로 요청을 보내고, 받은 데이터는 어떻게 화면에 표시하는지 살펴봤다.
이번엔 요청 받은 컨트롤러가 데이터를 어떻게 처리하는지 알아보자.
[데이터 흐름도]
데이터 흐름은 다음과 같다.
게시판 리스트 데이터 흐름도 처음 포인트 페이지에 접근할 때 경로는 다음과 같았다.
포인트 페이지 태그 [Controller - pointList.do]
해당 경로로 매핑된 코드는 다음과 같다.
포인트 리스트를 표시하는데 필요한 코드들만 보도록 하겠다.
@RequestMapping("/mypage/pointList.do") public ModelAndView pointList(@RequestParam(value = "pageNum", defaultValue = "1") int currentPage, @RequestParam(value = "keyfield", defaultValue = "1") String keyfield, HttpSession session) { ModelAndView mav = new ModelAndView(); MemberVO user = (MemberVO)session.getAttribute("user"); Map<String, Object> map = new HashMap<String, Object>(); map.put("keyfield", keyfield); map.put("mem_num", user.getMem_num()); int count = mypageService.selectPointListCountByMemNum(map); PagingUtil page = new PagingUtil(keyfield, null, currentPage, count, 5, 5, "pointList.do"); List<PayVO> list = null; if(count > 0) { map.put("start", page.getStartRow()); map.put("end", page.getEndRow()); map.put("keyfield", keyfield); list = mypageService.selectPointListByMemNum(map); } mav.addObject("count", count); mav.addObject("list", list); mav.addObject("page", page.getPage()); mav.setViewName("pointList"); return mav; }
코드를 살펴보자.
[@RequestParam]
가장 먼저 @RequestParam 어노테이션이 보일 것이다.
프로필 사진 구현 코드 리뷰 때 @ModelAttribute와 함께 잠시 언급을 했지만(여기),
이 어노테이션은 클라이언트에서 서버로 요청을 보낼 때, 어떤 값들을 같이 보내는 경우가 있는데 이 값들을 컨트롤러 파라미터에 전달 받을 때 사용한다.
@RequestParam이 붙은 파라미터는 클라이언트로부터 요청을 받을 때, 필수적으로 받아야 하는 파라미터가 된다.
위 코드를 예로 보면, int currentPage와 String keyfiled는 클라이언트로부터 필수적으로 받겠다는 뜻이다.
만약 하나라도 클라이언트에서 보내지 않으면 다음과 같이 오류가 뜬다.
@RequestParam 값 없을 시 오류 필요로하는 파라미터 pageNum이 존재하지 않는다고 뜬다.
하지만 위에서 포인트 페이지에 접근하는 태그 주소를 보면, 컨트롤러를 호출 할 때 어떤 값도 같이 보내고 있지 않다.
그래도 오류는 발생하지 않는데, 이는 @RequestParam뒤에 defaultValue를 설정해줘서 그렇다.
종합적으로 보자면 @RequestParam(value = "pageNum", defaultValue = "1") int currentPage 라는 코드는
필수적으로 pageNum이라는 이름으로 들어오는 값을 받아서 currentPage라는 변수에 할당을 할 것인데, 만약 pageNum이라는 이름으로 값이 들어오지 않으면 defaultValue 값인 1을 currentPage에 할당해 주겠다는 뜻이다.
이렇게 사용하면 클라이언트에서 값을 전송하지 않아도 defaultValue설정을 통해 오류 없이 사용할 수 있다.
[ModelAndView]
다음으로 ModelAndView 객체를 생성하고 있다.
ModelAndView 객체에 대한 설명은 다음과 같다.
ModelAndView 클래스 설명 MVC 프레임워크에서 모델과 뷰를 둘다 담을 수 있고, 하나의 값으로 리턴할 수 있도록 해준다고 한다.
처음 배울 때 파라미터에 Model model을 넣은 후 마지막에 String으로 view를 리턴하는 것과 ModelAndView 객체를 생성해서 객체를 리턴하는 것이 뭐가 다른지 몰랐다.
결론적으론 똑같은 것 같아서 선호에 따라 주로 model을 파라미터에 넣고, String view를 리턴하는 방식을 많이 사용했다.
지금 찾아보니 ModelAndView를 사용하는 것은 구식이라고 한다. 딱히 상관은 없는 것 같으니 선호대로 쓰면 될 것 같다.
참고 : https://backendcode.tistory.com/253
[List count]
다음은 DB에 저장되어 있는 리스트의 총 개수를 알아야 페이징 처리를 할 수 있으므로 관련 코드를 보자.
Map<String, Object> map = new HashMap<String, Object>(); map.put("keyfield", keyfield); map.put("mem_num", user.getMem_num()); int count = mypageService.selectPointListCountByMemNum(map);
코드는 위와 같았다.
먼저 key/value 쌍으로 데이터를 보내주기 위해 Map 객체를 생성했다.
회원 번호를 기준으로 DB에서 데이터를 찾을 것이므로 회원 번호를 mem_num이라는 key로 넣어줬고, 정렬 필드에 따라 다른 데이터들을 꺼내오기 위해 keyfield도 할당했다.
마지막으로 주입 받은 서비스를 호출해 map객체를 넘겨주는 것으로 코드는 끝이다.
[Mapper.xml]
이번엔 쿼리문을 작성한 Mapper.xml을 보자.
진행한 프로젝트에서 Mybatis를 사용했기 때문에 xml파일에 쿼리문을 작성하고 Mapper.java를 interface로 정의한 뒤 @Mapper로 등록해줬다.
찾다보니 잘 정리해둔 포스트가 있어 참고로 적어둔다.
참고 : https://velog.io/@mumuni/MyBatis를-Springboot에서-사용해보자
코드는 다음과 같다.
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="kr.spring.mypage.dao.MypageMapper"> <select id="selectPointListCountByMemNum" parameterType="map" resultType="integer"> SELECT COUNT(*) FROM point WHERE mem_num = #{mem_num} <if test="keyfield == 1"></if> <if test="keyfield == 2"> <![CDATA[ AND point_point > 0 ]]> </if> <if test="keyfield == 3"> <![CDATA[ AND point_point < 0 ]]> </if> </select> </mapper>
이어서 mapper.java도 보자.
@Mapper public interface MypageMapper { public int selectPointListCountByMemNum(Map<String, Object> map); }
프로필 이미지 등록 및 수정 코드는 mapper.java에서 어노테이션을 이용해 쿼리문을 작성했다(다음은 예시 코드).
@Update("UPDATE member_detail SET mem_photo=#{mem_photo}, mem_photo_name = #{mem_photo_name} WHERE mem_num=#{mem_num}") public void updateProfile(MemberVO member);
하지만 어노테이션을 이용하면 특정 조건에 따라 쿼리문이 달라져야할 경우 처리하기 어렵다.
이럴 때 xml파일에 위와 같이 쿼리문을 정의해서 사용할 수 있다.
interface로 mapper.java를 정의하고 xml에 쿼리문을 작성해서 사용하려면 위와 같이 java에 선언된 메소드 명과 xml에 정의한 id명이 같아야 한다.
차례로 설명하면,
- DB에서 원하는 데이터를 불러올 것이므로 <select></select>를 사용한다.
- id는 메소드 명, parameterType은 전달 받는 파라미터 타입, resultType은 리턴하는 값 타입을 입력한다.
- 아래 원하는 sql문을 적는다.
- 조건에 따라 sql문이 달라져야 한다면 if태그를 넣고 test = "원하는 조건"을 써준다.
- 태그를 명시할 때 화살괄호를 사용하므로 대소비교 코드 작성시 <![CDATA[코드]]>를 사용한다.
CDATA는 character data를 뜻하며, xml은 HTML과 같이 마크업 언어(ML, Markup Language: 태그 등을 이용하여 문서나 데이터의 구조를 명기하는 언어의 한 가지. 출처: 위키백과)이다.
태그 기반으로 코드를 작성하기 때문에 태그 기호와 문자 기호를 구분해 줘야 한다.
이 때 CDATA를 이용하면, 해당 블록 안에 있는 코드는 문자 기호로 인식한다.
종합적으로 쿼리문을 해석해보면,
point라는 테이블의 모든 데이터의 개수를 가져올 것인데, keyfield 값이 1이면 조건 없이, 2이면 point_point 열의 값이 양수인, 3이면 point_point 열의 값이 음수인 행을 선택하겠다는 뜻이다.
이렇게 하면 DB에서 로그인 한 회원의 포인트 적립 및 사용 내역 개수를 불러와 int count로 선언된 변수에 담게 된다.
서비스단은 코드만 적고 넘어가도록 하자.
[Service]
public interface MypageService { public int selectPointListCountByMemNum(Map<String, Object> map); }
[ServiceImpl]
@Service @Transactional public class MypageServiceImpl implements MypageService{ @Autowired private MypageMapper mypageMapper; @Override public int selectPointListCountByMemNum(Map<String, Object> map) { return mypageMapper.selectPointListCountByMemNum(map); } }
[Controller]
@Controller public class MypageController { @Autowired private MypageService mypageService; }
이번 글에서는 요청 받은 컨트롤러에서 데이터가 어떻게 처리되는지 일부 정리했다.
다음 글에서는 이어서 리스트를 불러오는 과정을 알아보고, 마지막에 PagingUtil 클래스 내부 코드를 보도록 하겠다.
'프로젝트 > 기능 정리' 카테고리의 다른 글