프로젝트/기능 정리

[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명이 같아야 한다.

 

차례로 설명하면,

 

  1. DB에서 원하는 데이터를 불러올 것이므로 <select></select>를 사용한다.
  2. id는 메소드 명, parameterType은 전달 받는 파라미터 타입, resultType은 리턴하는 값 타입을 입력한다.
  3. 아래 원하는 sql문을 적는다.
  4. 조건에 따라 sql문이 달라져야 한다면 if태그를 넣고 test = "원하는 조건"을 써준다.
  5. 태그를 명시할 때 화살괄호를 사용하므로 대소비교 코드 작성시 <![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 클래스 내부 코드를 보도록 하겠다.