ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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 클래스 내부 코드를 보도록 하겠다.

Designed by Tistory.