[Java] SpringBoot 프로젝트 : 리스트 페이징 처리(2) - DB 리스트 개수 가져오기(@RequestParam, ModelAndView, xml)
목적
- 게시판 형식 웹사이트 개발 시 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는 클라이언트로부터 필수적으로 받겠다는 뜻이다.
만약 하나라도 클라이언트에서 보내지 않으면 다음과 같이 오류가 뜬다.
필요로하는 파라미터 pageNum이 존재하지 않는다고 뜬다.
하지만 위에서 포인트 페이지에 접근하는 태그 주소를 보면, 컨트롤러를 호출 할 때 어떤 값도 같이 보내고 있지 않다.
그래도 오류는 발생하지 않는데, 이는 @RequestParam뒤에 defaultValue를 설정해줘서 그렇다.
종합적으로 보자면 @RequestParam(value = "pageNum", defaultValue = "1") int currentPage 라는 코드는
필수적으로 pageNum이라는 이름으로 들어오는 값을 받아서 currentPage라는 변수에 할당을 할 것인데, 만약 pageNum이라는 이름으로 값이 들어오지 않으면 defaultValue 값인 1을 currentPage에 할당해 주겠다는 뜻이다.
이렇게 사용하면 클라이언트에서 값을 전송하지 않아도 defaultValue설정을 통해 오류 없이 사용할 수 있다.
[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 클래스 내부 코드를 보도록 하겠다.