무한 스크롤을 구현하는 방법에는 Offset과 NonOffset 방식이 있는데 Offset의 경우 전체를 조회해서 선택하기 때문에 데이터양이 많을 경우 불러오는데 성능이 떨어질 수 있다.
최적화를 위해서는 NonOffset 방식을 사용하는 것이 좋다.
PageNation.js
//현재 스크롤 위치 저장
let lastScroll = 0;
let page = 1; // 기본 시작값 (고정)
let nowPageLimit = 0;
let nextPageLimit = 0;
let beforePageLimit = parseInt($('#PgInfo').val()); // 기본 종료값 ()
// let beforePageLimit = 4; // 기본 종료값 (pageInfo 변경될 때마다 변경 필요함 PageInfo.ROW_OF_PAGE)
let bucket = $('input[name=bucket]').val();
//데이터 가져오는 함수
function getData(limit){
//다음페이지
nextPageLimit = (page + 1) * limit;
var currentScroll = $(this).scrollTop();
$.ajax({
type: "POST",
enctype: 'multipart/form-data',
url: "/api/getNotice",
async : false,
data: {
"end" : nextPageLimit,
"start" : beforePageLimit+1
},
success: function(data) {
let html = ``;
data.forEach((notice, idx) => {
html =
`<tr>
<input type="hidden" name="noti_no" value="${notice.noti_no}">
<td class="tbcol ncol1">
<input type="text" name="subject" value=" ${notice.subject}"
class="noticeSub noticeDetail" readonly="readonly">
</td>
<td class="tbcol ncol2">
<input type="text" name="writer" value="${notice.writer}"
class="noticeDetail" readonly="readonly">
</td>
<td class="tbcol ncol3">
<input type="text" name="regdate" value="${notice.regdate}"
class="number" readonly="readonly">
</td>
<td class="tbcol ncol4">
<input type="text" name="vdate" value="${notice.vdate}"
class="number" readonly="readonly">
</td>
<td class="tbcol ncol5">
<input type="text" name="readcount" value="${notice.readcount}"
class="" readonly="readonly">
</td>
</tr>`;
$("tbody").append(html);
});
},
error: function (data, status, err) {
alert("페이지 불러오기에 실패했습니다.");
}, complete: function(){
page += 1;
beforePageLimit = beforePageLimit + limit;
}
});
}
$(document).scroll(function(e) {
var currentScroll = $(this).scrollTop(); //현재 높이 저장
var documentHeight = $(document).height(); //전체 문서의 높이
var nowHeight = $(this).scrollTop() + $(window).height(); //(현재 화면상단 + 현재 화면 높이)
if(currentScroll > lastScroll) { //스크롤이 아래로 내려갔을때만 해당 이벤트 진행.
//nowHeight을 통해 현재 화면의 끝이 어디까지 내려왔는지 파악가능
//즉 전체 문서의 높이에 일정량 근접했을때 글 더 불러오기)
if(documentHeight < (nowHeight + (600))) {
// 기존 값 : documentHeight*0.2, 현재 값 : 600
//함수콜
getData(8); // 증가값
}
}
//현재위치 최신화
lastScroll = currentScroll;
});
아래는 서버단의 API 코드이다.
RestAPIController.java
@RestController
@Slf4j
public class RestApiController {
@Autowired
NoticeService noticeService;
@PostMapping("/api/getNotice")
public List<NoticeVO> getNotice(NoticeVO nvo) {
List<NoticeVO> noticeList = noticeService.getNotice(nvo);
return noticeList;
}
}
그리고 해당 기능은 기존의 프로젝트하면서 적용한 내용이기 때문에 프로젝트 기준으로 작성되어있다.
그래서 Service와 ServiceImpl로 구현되어 있다.
자세한 사항
배우는 입장으로써 구현한 것이다. 이유는 다음과 같다.
- 인터페이스와 구현 클래스를 분리할 수 있다.
- 스프링 프레임워크가 제공하는 IoC(Inversion of Control) 기능과 함께 사용할 수 있다.
아무튼 ServiceImpl 쪽 코드를 보면 Notice
NoticeServiceImpl.java
@Slf4j
@Service
public class NoticeServiceImpl implements NoticeService {
@Autowired
private NoticeDao noticeDao;
@Override
public List<NoticeVO> getNotice(NoticeVO nvo) {
List<NoticeVO> NoticeList = noticeDao.getNoticeList(nvo);
return NoticeList;
}
}
혹시 몰라 NoticeVO 코드도 올려놓았다.
NoticeVO.java
@Data
@ToString
public class NoticeVO {
private int rn;
private int noti_no;
private int readcount;
private String subject;
private String content;
private String writer;
private String vdate;
private String regdate;
private String state;
private String text;
private int start;
private int end;
}
Dao또한 인터페이스와 구현체인 Mapper 나눠져 있다. 파일 구조 자체가 현재 유연성을 높이기 위해서 전부 인터페이스로 나눠놓은 상태이다.
NoticeDao.java
@Mapper
public interface NoticeDao {
List<NoticeVO> getNoticeList(NoticeVO nvo);
}
NoticeMapper.xml
<?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="com.ecom6.dao.notice.NoticeDao">
<select id="getNoticeList" parameterType="nvo" resultType="nvo">
SELECT * FROM (
SELECT ROWNUM RN, A.* FROM(
SELECT NOTI_NO, SUBJECT, CONTENT, READCOUNT, WRITER,
VDATE, REGDATE, STATE
FROM NOTICET
WHERE TO_DATE(VDATE) >= SYSDATE
AND STATE = 'A'
<if test="text!=null and text!=''">
AND SUBJECT LIKE '%'||#{text}||'%'
</if>
) A
)
<![CDATA[
WHERE RN >= #{start} AND RN <= #{end}
]]>
</select>
</mapper>
나중에 GIF를 추가해야겠다.