VO 활용 2. 관리자 페이지에 재생수가 많은 순서로 Top 10 출력
- 2가지 방법이 있다
1. 재생수를 기준으로 내림차순 정렬한 전체 조회 결과 중 일부만 View에 표시
(전체 조회 결과를 가져오기 때문에 성능이 떨어진다)
2. Top N Query를 사용
Top N Query를 사용하는 방법
MusicDao
public interface MusicDao {
// 추상 메소드 - 음원 Top 10
List<MusicDto> selectTopTen();
}
MusicDaoImpl
@Repository
public class MusicDaoImpl implements MusicDao {
// 의존성 주입
@Autowired
JdbcTemplate jdbcTemplate;
// 추상 메소드 오버라이딩 - 음원 Top 10
@Override
public List<MusicDto> selectTopTen() {
String sql = "select * from ("
+ "select TMP.*, rownum music_rate from ("
+ "select * from music order by music_play desc"
+ ")TMP"
+ ") where music_rate between 1 and 10";
return jdbcTemplate.query(sql, mapper);
}
}
AdminController
@Controller
@RequestMapping("/admin")
public class AdminController {
// 의존성 주입
@Autowired
private MusicDao musicDao;
// 음원 Top 10 Mapping
@GetMapping("/music/play")
public String musicPlay(Model model) {
// model에 음원 Top 10 조회 결과 첨부
model.addAttribute("list", musicDao.selectTopTen());
// 음원 현황 페이지(play.jsp)로 연결
return "admin/music/play";
}
}
VO 활용 2-1. 관리자 페이지에 재생수가 많은 순서로 Top N 출력
MusicDao
public interface MusicDao {
// 추상 메소드 - 음원 Top N
List<MusicDto> selectTopN(int limit);
}
MusicDaoImpl
@Repository
public class MusicDaoImpl implements MusicDao {
// 의존성 주입
@Autowired
JdbcTemplate jdbcTemplate;
// 추상 메소드 오버라이딩 - 음원 Top N
@Override
public List<MusicDto> selectTopN(int limit) {
String sql = "select * from ("
+ "select TMP.*, rownum music_rate from ("
+ "select * from music order by music_play desc"
+ ")TMP"
+ ") where music_rate between 1 and ?";
Object[] param = new Object[] {limit};
return jdbcTemplate.query(sql, mapper, param);
}
}
AdminController
@Controller
@RequestMapping("/admin")
public class AdminController {
// 의존성 주입
@Autowired
private MusicDao musicDao;
// 음원 Top N Mapping
@GetMapping("/music/play/{limit}")
public String musicPlay(Model model, @PathVariable int limit) {
// model에 음원 Top N 조회 결과 첨부
model.addAttribute("list", musicDao.selectTopN(limit));
// 음원 현황 페이지(play.jsp)로 연결
return "admin/music/play";
}
}
- @PathVariable 또는 @RequestParam으로 Top N 조회를 위한 매개변수 limit을 입력받을 수 있다
- @PathVariable을 사용할 때 Mapping 주소 끝에 /{경로변수}가 붙는다
VO 활용 2-2. 관리자 페이지에 재생수가 많은 순서로 Top N to N 출력
MusicDao
public interface MusicDao {
// 추상 메소드 - 음원 Top N to N
List<MusicDto> selectTopNtoN(int begin, int end);
}
MusicDaoImpl
@Repository
public class MusicDaoImpl implements MusicDao {
// 의존성 주입
@Autowired
JdbcTemplate jdbcTemplate;
// 추상 메소드 오버라이딩 - 음원 Top N to N
@Override
public List<MusicDto> selectTopNtoN(int begin, int end) {
String sql = "select * from ("
+ "select TMP.*, rownum music_rate from ("
+ "select * from music order by music_play desc"
+ ")TMP"
+ ") where music_rate between ? and ?";
Object[] param = new Object[] {begin, end};
return jdbcTemplate.query(sql, mapper, param);
}
}
AdminController
@Controller
@RequestMapping("/admin")
public class AdminController {
// 의존성 주입
@Autowired
private MusicDao musicDao;
// 음원 Top N to N Mapping
@GetMapping("/music/play2")
public String musicPlay2(Model model,
@RequestParam(required = false, defaultValue = "1") int begin,
@RequestParam(required = false, defaultValue = "10") int end) {
// model에 음원 Top N to N 조회 결과 첨부
model.addAttribute("list", musicDao.selectTopNtoN(begin, end));
// 음원 현황 페이지(play.jsp)로 연결
return "admin/music/play";
}
}
- @RequestParam의 필수 여부(required)와 입력하지 않았을 때의 기본값(defaultValue)를 설정할 수 있다
AdminController - 간단한 페이징 구현
@Controller
@RequestMapping("/admin")
public class AdminController {
// 의존성 주입
@Autowired
private MusicDao musicDao;
// 음원 Top N to N Mapping - 페이징 테스트
@GetMapping("/music/play3")
public String musicPlay3(Model model,
@RequestParam(required = false, defaultValue = "1") int page,
@RequestParam(required = false, defaultValue = "10") int size) {
// 페이지 번호(page)와 한 페이지당 표시 음원 수(size)에 대하여
// 페이지의 마지막 음원 번호(end)는 페이지 번호(page) * 표시 음원 수(size)
int end = page * size;
// 페이지의 처음 음원 번호(begin)은 페이지의 마지막 음원 번호(end) - [표시 음원 수(size) - 1]
int begin = end - (size - 1);
// model에 음원 Top N to N 조회 결과 첨부
model.addAttribute("list", musicDao.selectTopNtoN(begin, end));
// 음원 현황 페이지(play.jsp)로 연결
return "admin/music/play";
}
}
play.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<jsp:include page = "/WEB-INF/views/template/adminHeader.jsp">
<jsp:param name = "title" value = "인기 음원 Top ${list.size()}"/>
</jsp:include>
<h1>인기 음원 Top ${list.size()}</h1>
<a href = "/admin/music/play/3">Top 3</a>
<a href = "/admin/music/play/5">Top 5</a>
<a href = "/admin/music/play/10">Top 10</a>
<a href = "/admin/music/play/20">Top 20</a>
<table border = "1" width = "300">
<thead>
<tr>
<th>순위</th>
<th>음원명</th>
<th>가수명</th>
<th>재생수</th>
</tr>
</thead>
<tbody align = "center">
<c:forEach var = "musicDto" items = "${list}" varStatus="status">
<tr>
<td>${status.count}</td>
<td>${musicDto.getMusicTitle()}</td>
<td>${musicDto.getMusicArtist()}</td>
<td>${musicDto.getMusicPlay()}</td>
</tr>
</c:forEach>
</tbody>
</table>
<jsp:include page = "/WEB-INF/views/template/adminFooter.jsp"></jsp:include>
VO 활용 3. 관리자 페이지에 연도별 음원 발매 숫자 출력 (최신 연도순)
- 순위(rank), 년도(year), 해당 년도 음원 발매수(cnt)를 위한 VO 생성
MusicYearCountVO
public class MusicYearCountVO {
// 필드
private int rank;
private int year;
private int cnt;
// 생성자
public MusicYearCountVO() {
super();
}
// getter & setter
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getCnt() {
return cnt;
}
public void setCnt(int cnt) {
this.cnt = cnt;
}
}
MusicDao
public interface MusicDao {
// 추상 메소드 - 연도별 발매수 + 순위
List<MusicYearCountVO> releaseByYearWithRank();
}
MusicDaoImpl
@Repository
public class MusicDaoImpl implements MusicDao {
// 의존성 주입
@Autowired
JdbcTemplate jdbcTemplate;
// 추상 메소드 오버라이딩 - 연도별 발매수 + 순위
@Override
public List<MusicYearCountVO> releaseByYearWithRank() {
String sql = "select TMP.*, rank() over(order by cnt desc) rank from ("
+ "select "
+ "extract(year from release_time) year, "
+ "count(*) cnt "
+ "from music "
+ "group by extract(year from release_time) "
+ "order by year desc "
+ ")TMP";
return jdbcTemplate.query(sql, countMapper);
}
}
- SQL문에서 년도 추출 구문
1) to_char(release_title, 'yyyy')
2) extractor(year from release_title)
AdminController
@Controller
@RequestMapping("/admin")
public class AdminController {
// 의존성 주입
@Autowired
private MusicDao musicDao;
// 음원 연도별 발매수 및 순위 Mapping
@GetMapping("/music/release")
public String musicRelease(Model model) {
// model에 음원 연도별 발매수 + 순위 조회 결과 첨부
model.addAttribute("list", musicDao.releaseByYearWithRank());
// 음원 발매 현황 페이지(play.jsp)로 연결
return "admin/music/release";
}
}
JUnit
- Java용 단위 테스트 도구
- 단위 테스트(Unit Test) : 소스 코드의 특정 모듈(프로그램 내 하나의 기능)이 의도된 대로 정확히 작동하는지 검증
Test 생성
1) Test용 클래스 생성 후 클래스명 위에 @SpringBootTest 작성 - 통합 테스트 환경 설정
2) Test용 메소드를 작성한 후 메소드명 위에 @Test 작성 - 테스트 할 메소드임을 명시 (반환형은 반드시 void)
- @Test가 여러 개일 경우 모든 Test가 성공해야 성공으로 표시된다
3) 매번 Test용 메소드를 실행하기 전 실행해야 할 메소드명 위에 @BeforeEach 작성
매번 Test용 메소드를 실행한 후 실행해야 할 메소드명 위에 @AfterEach 작성
** Assertions 클래스- Test의 결과를 주장(assert)하여 그 결과가 주장과 같으면 true, 다르면 false를 출력- Assertions 클래스 메소드의 일부
boolean | assertEquals(자료형 expected, 자료형 actual) | expected가 actual과 같으면 true를 출력 |
boolean | assertNotEquals(자료형 expected, 자료형 actual) | expected가 actual과 다르면 true를 출력 |
boolean | assertNull(Object expected) | expected가 null이면 true를 출력 |
boolean | assertNotNull(Object expected) | expected가 null이 아니면 true를 출력 |
ex) Pocketmon 입력(INSERT) 기능 테스트
@SpringBootTest
public class Test03 {
// 의존성 주입
@Autowired
private PocketMonsterDao pocketMonsterDao;
// 의존성 주입이 정상적으로 되었는지
// - pocketMonsterDao가 null이 아니다
@Test
public void test01() {
assertNotNull(pocketMonsterDao);
}
// 등록(INSERT) 실행
@Test
public void test02() {
pocketMonsterDao.insert(60, "발챙이", "물");
}
}
MockMvc 클래스
- 가짜 객체를 만들어 어플리케이션을 서버에 배포하지 않고도 Spring MVC의 동작을 재현할 수 있는 클래스
- 가짜의 HTTP 요청을 컨트롤러에 보내고 서버 내에서 컨트롤러를 실행하지 않고 컨트롤러의 동작을 테스트
- MockMvc 클래스 메소드의 일부
ResultActions | perform(RequestBuilder requestBuilder) | 요청을 수행하고 그 결과를 반환한다 |
MockMvcBuilders 클래스
- 주어진 WebApplcationContext를 매개변수로 하여 MockMvc의 인스턴스를 생성
- MockMvcBuilders 클래스 메소드의 일부
static DefaultMockMvcBuilder | webAppContextSetup(WebApplicationContext context) | MockMvc의 인스턴스 생성 |
WebApplicationContext 인터페이스
- DispatcherServlet이 직접 사용하는 Controller, Repository를 포함한 웹 관련 빈을 등록하는 데 사용
(Test에서는 Controller를 등록하기 위해 사용하는 것 같다)
ex) 수동으로 환경 설정을 하여 Controller 테스트
@SpringBootTest
public class Test05 {
// MockMvc의 인스턴스 생성
private MockMvc mockMvc;
// 의존성 주입
@Autowired
private WebApplicationContext ctx;
@BeforeEach
public void before() {
// WebApplicationContext을 매개변수로 하여 MockMvc의 인스턴스 생성
mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build();
}
@Test
public void test() throws Exception {
// Assertion 대신 MockMvc의 perform()의 결과를 반환하도록 한다
mockMvc.perform(get("/pocketmon/insert")) // get 요청 생성
.andExpect(status().is(200)) // 상태가 200번대인 경우 true 반환
.andDo(print()) // 그 결과를 출력(print)한다
.andReturn();
}
}
ex) 자동으로 환경 설정을 하여 Controller 테스트
@SpringBootTest
@AutoConfigureMockMvc // MockMvc에 대한 자동 설정
public class Test06 {
// 의존성 주입
@Autowired
private MockMvc mockMvc;
@Test
public void test() throws Exception {
// Assertion 대신 MockMvc의 perform()의 결과를 반환하도록 한다
mockMvc.perform(get("/pocketmon/insert")) // get 요청 생성
.andExpect(status().is2xxSuccessful()) // 상태가 2xx인 경우만 성공
.andDo(print()) // 그 결과를 출력(print)한다
.andReturn();
}
}
Lombok
의존성 추가
1) 해당 project 우클릭 - spring - Add Starters 선택
2) lombok 검색 후 선택
3) pom.xml 선택
4) lombok이 추가되었는지 확인
주요 Annotation
@Getter | getter 메소드 생성 |
@Setter | setter 메소드 생성 |
@ToString | toString 메소드 오버라이딩 |
@Data | @Getter + @Setter + @ToString |
@NoArgsConstructor | 기본 생성자 생성 |
@AllArgsConstructor | 모든 필드를 매개변수로 하는 생성자 생성 |
@Builder | Builder 패턴을 이용하기 위한 Annotation |
- @NoArgsConstructor, @AllArgsConstructor, @Builder는 Builder 패턴을 사용하기 위해 쓰인다
생성자를 이용한 인스턴스 생성과 Builder 패턴을 이용한 인스턴스 생성 비교
@SpringBootTest
public class Test08 {
@Test
public void test() {
// 기본 생성자를 이용한 인스턴스 생성
Student s = new Student();
s.setName("피카츄");
s.setScore(50);
// Builder를 이용한 인스턴스 생성
Student a = Student.builder().name("피카츄").score(50).build();
}
}
- 기본 생성자를 이용한 인스턴스 생성
1) 기본 생성자를 이용하여 인스턴스 생성
2) setter 메소드로 필드값을 설정
- Builder를 이용한 인스턴스 생성
- 클래스명.builder().필드1(세팅값).필드2(세팅값)...필드n(세팅값).build();
- 1줄의 코드로 인스턴스를 생성할 수 있다
'국비교육 > 국비교육' 카테고리의 다른 글
day38 - 0919 (0) | 2022.09.20 |
---|---|
day37 - 0916 (0) | 2022.09.16 |
day35 - 0914 (0) | 2022.09.14 |
day34 - 0913 (0) | 2022.09.09 |
day33 - 0908 (0) | 2022.09.08 |