Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- baeldung
- 리눅스
- 서버설정
- d
- resttemplate
- 구멍가게코딩단
- 알파회계
- ㅒ
- 선형대수
- iterator
- 코드로배우는스프링웹프로젝트
- 자바편
- 스프링 시큐리티
- network configuration
- 네트워크 설정
- 이터레이터
- 처음 만나는 AI수학 with Python
- 페이징
- 친절한SQL튜닝
- /etc/network/interfaces
- 티스토리 쿠키 삭제
- 데비안
- 코드로배우는스프링부트웹프로젝트
- 스프링부트핵심가이드
- 자료구조와 함께 배우는 알고리즘 입문
- 처음 만나는 AI 수학 with Python
- 자료구조와함께배우는알고리즘입문
- 목록처리
- Kernighan의 C언어 프로그래밍
- GIT
Archives
- Today
- Total
bright jazz music
blog08: 페이징 처리 본문
페이징 처리
@PageableDafault를 컨트롤러에 달아 페이징 관련 파라미터를 받아서그걸 서비스에 넘긴 뒤에 JpaRepository를 사용해서 데이터를 가져오는 작업을 했다.
----------
//PostController.java
package com.endofma.blog.controller;
import com.endofma.blog.domain.Post;
import com.endofma.blog.request.PostCreate;
import com.endofma.blog.response.PostResponse;
import com.endofma.blog.service.PostService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@RestController
@RequiredArgsConstructor
public class PostController {
private final PostService postService;
@PostMapping("/posts")
public void post(@RequestBody @Valid PostCreate request) {
postService.write(request);
// return Map.of();
}
//단건 조회
@GetMapping("/posts/{postId}")
public PostResponse get(@PathVariable Long postId){
PostResponse response = postService.get(postId);
return response;
}
//여러 글 조회(글 목록 가져오기)
// /posts
@GetMapping("/posts")
//글이 너무 많은 경우 비용이 너무 많이 든다.
//DB가 뻗을 수 있음.
//DB -> 애플리케이션 서버로 전달하는 시간, 트래픽 비용이 많이 발생할 수 있다.
//따라서 페이지 설정
// 원래는 int로 받았음
// public List<PostResponse> getList(@RequestParam int page){
// 그러나 사용의 용이성을 위해 pageable을 사용함
// public List<PostResponse> getList(@PageableDefault Pageable pageable){ //1로 넘겨도 0으로 보정해서 넣어줌.
//근데 PageableDefault의 기본 size가 10이라 yml에서 설정해도 먹히지 않는다.
//이 떄는 어노테이션을 그대로 유지하면서 size를 파라미터로 넣어주는 방법이 있다.
//public List<PostResponse> getList(@PageableDefault(size=10) Pageable pageable){
//또는 어노테이션을 빼고 application.yml에서 default-page-size를 설정하여 해결할 수 수있다.
public List<PostResponse> getList(Pageable pageable){
return postService.getList(pageable);
}
}
//PostService.java
package com.endofma.blog.service;
import com.endofma.blog.domain.Post;
import com.endofma.blog.repository.PostRepository;
import com.endofma.blog.request.PostCreate;
import com.endofma.blog.response.PostResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Slf4j
@Service
//@RequiredArgsConstructor
public class PostService {
private final PostRepository postRepository;
public PostService(PostRepository postRepository){
this.postRepository = postRepository;
}
public void write(PostCreate postCreate) {
//PostCreate 일반 클래스 ==> Post 엔티티
Post post = Post.builder()
.title(postCreate.getTitle())
.content(postCreate.getContent())
.build();
postRepository.save(post);
}
//단건 조회
public PostResponse get(Long id) {
Post post = postRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 글입니다.")); //있으면 post반환 없으면 에러 반환
//응답 클래스를 분리
return PostResponse.builder()
.id(post.getId())
.title(post.getTitle())
.content(post.getContent())
.build();
}
// public List<Post> getList() {
// return postRepository.findAll();
// }
public List<PostResponse> getList(Pageable pageable){
//application.yml 파일의 설정처럼 web요청에 1로 왔을 때 내부적으로는 0으로 변경해야 한다.
//그러나 파라미터를 수동으로 받기 때문에 동작하지 않는다.
//이 때는 컨트롤러에서 @RequestParam으로 페이지를 받지 않고 @Pageabledefault로 받으면 된다.
//Pageable로 받으면 아래 코드는 필요 없어진다.
// Pageable pageable = PageRequest.of(page, 5, Sort.by(Sort.Direction.DESC, "id")); //springframework.data.domain
return postRepository.findAll(pageable).stream() //pageable
.map(PostResponse::new)
.collect(Collectors.toList());
}
}
#application.yml
spring:
h2:
console:
enabled: true
path: /h2-console
#0이 아닌 1부터 시작하는 걸로 하겠다는 의미
data:
web:
pageable:
one-indexed-parameters: true
default-page-size: 5
datasource:
url: jdbc:h2:mem:blog
username: sa
password:
driver-class-name: org.h2.Driver
//PostServiceTest.java
package com.endofma.blog.service;
@SpringBootTest
class PostServiceTest {
@Autowired
private PostService postService;
@Autowired
private PostRepository postRepository;
@BeforeEach
void clean(){
postRepository.deleteAll();
}
//...
@Test
@DisplayName("글 1페이지 조회")
void test3(){
//given
List<Post> requestPost = IntStream.range(1, 31) //for (int =0; i<30; i++)
.mapToObj(i -> Post.builder()
.title("블로그 제목 " + i)
.content("제이드 " + i)
.build()).collect(Collectors.toList());
postRepository.saveAll(requestPost);
// sql -> select, limit, offset 알아야 함.
Pageable pageable = PageRequest.of(0, 5, Sort.Direction.DESC, "id");
//when
List<PostResponse> posts = postService.getList(pageable);
//then
assertEquals(5L, posts.size());
assertEquals("블로그 제목 30", posts.get(0).getTitle());
assertEquals("블로그 제목 26", posts.get(4).getTitle());
}
}
//PostControllerTest.java
//...
//@WebMvcTest //간단한 웹테스트만 가능. 우린 스프링 전반에 걸쳐 여러 가지를 만들었기 때문에 @SpringBootTest가 필요
@AutoConfigureMockMvc //@WebMvcTest가 없어지면 기존 테스트가 안되므로 @WebMvcTest를 구성하는 애노테이션을 떼내어 붙였다.
@SpringBootTest
class PostControllerTest {
@Autowired
private MockMvc mockMvc; ////Could not autowire. No beans of 'MockMvc' type found.
@Autowired
private PostRepository postRepository; //DB저장 테스트를 위해 주입
@BeforeEach
void clean() {
postRepository.deleteAll();
}
//...
@Test
@DisplayName("글 여러 개 조회")
void test5() throws Exception{
//given
List<Post> requestPost = IntStream.range(1, 31) //for (int =0; i<30; i++)
.mapToObj(i -> Post.builder()
.title("블로그 제목 " + i)
.content("제이드 " + i)
.build()).collect(Collectors.toList());
postRepository.saveAll(requestPost);
//expected
// mockMvc.perform(get("/posts?page=1")
mockMvc.perform(get("/posts?page=1&sort=id,desc")
// mockMvc.perform(get("/posts?page=1&sort=id,desc&size=5")
//size를 매번 넣어주기 귀찮으면 application.yml에 가서 default-page-size를 설정해 준다.
//보통 현업에서는 10개보기 ,20개 보기 등의 옵션이 없으면 그냥 서버가 주는대로 받기 때문에
//size를 파라미터로 넘기는 경우가 많지는 않다.
.contentType(APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()", is(5)))
.andExpect(jsonPath("$[0].id").value(30))
.andExpect(jsonPath("$[0].title").value("블로그 제목 30"))
.andExpect(jsonPath("$[0].content").value("제이드 30"))
.andDo(print());
}
}
결과
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.6)
2023-01-04 09:22:58.931 INFO 40156 --- [ Test worker] c.e.blog.controller.PostControllerTest : Starting PostControllerTest using Java 11.0.12 on DESKTOP-8H1PTVG with PID 40156 (started by markany-hjcha in D:\personal\blog)
2023-01-04 09:22:58.932 INFO 40156 --- [ Test worker] c.e.blog.controller.PostControllerTest : No active profile set, falling back to 1 default profile: "default"
2023-01-04 09:22:59.539 INFO 40156 --- [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2023-01-04 09:22:59.586 INFO 40156 --- [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 40 ms. Found 1 JPA repository interfaces.
2023-01-04 09:23:00.055 INFO 40156 --- [ Test worker] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2023-01-04 09:23:00.260 INFO 40156 --- [ Test worker] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2023-01-04 09:23:00.324 INFO 40156 --- [ Test worker] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2023-01-04 09:23:00.383 INFO 40156 --- [ Test worker] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.6.14.Final
2023-01-04 09:23:00.621 INFO 40156 --- [ Test worker] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2023-01-04 09:23:00.769 INFO 40156 --- [ Test worker] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2023-01-04 09:23:01.689 INFO 40156 --- [ Test worker] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2023-01-04 09:23:01.699 INFO 40156 --- [ Test worker] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2023-01-04 09:23:02.232 WARN 40156 --- [ Test worker] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2023-01-04 09:23:02.482 INFO 40156 --- [ Test worker] o.s.b.a.h2.H2ConsoleAutoConfiguration : H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem:blog'
2023-01-04 09:23:02.748 INFO 40156 --- [ Test worker] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
2023-01-04 09:23:02.748 INFO 40156 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Initializing Servlet ''
2023-01-04 09:23:02.749 INFO 40156 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Completed initialization in 1 ms
2023-01-04 09:23:02.769 INFO 40156 --- [ Test worker] c.e.blog.controller.PostControllerTest : Started PostControllerTest in 4.163 seconds (JVM running for 6.511)
MockHttpServletRequest:
HTTP Method = GET
Request URI = /posts
Parameters = {page=[1], sort=[id,desc]}
Headers = [Content-Type:"application/json;charset=UTF-8"]
Body = null
Session Attrs = {}
Handler:
Type = com.endofma.blog.controller.PostController
Method = com.endofma.blog.controller.PostController#getList(Pageable)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json"]
Content type = application/json
Body = [{"id":30,"title":"블로그 제목 30","content":"제이드 30"},{"id":29,"title":"블로그 제목 29","content":"제이드 29"},{"id":28,"title":"블로그 제목 28","content":"제이드 28"},{"id":27,"title":"블로그 제목 27","content":"제이드 27"},{"id":26,"title":"블로그 제목 26","content":"제이드 26"}]
Forwarded URL = null
Redirected URL = null
Cookies = []
2023-01-04 09:23:03.263 INFO 40156 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-01-04 09:23:03.264 INFO 40156 --- [ionShutdownHook] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
2023-01-04 09:23:03.266 WARN 40156 --- [ionShutdownHook] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 90121, SQLState: 90121
2023-01-04 09:23:03.266 ERROR 40156 --- [ionShutdownHook] o.h.engine.jdbc.spi.SqlExceptionHelper : Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT=FALSE" to the db URL) [90121-214]
2023-01-04 09:23:03.267 WARN 40156 --- [ionShutdownHook] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 90121, SQLState: 90121
2023-01-04 09:23:03.267 ERROR 40156 --- [ionShutdownHook] o.h.engine.jdbc.spi.SqlExceptionHelper : Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT=FALSE" to the db URL) [90121-214]
2023-01-04 09:23:03.267 WARN 40156 --- [ionShutdownHook] o.s.b.f.support.DisposableBeanAdapter : Invocation of destroy method failed on bean with name 'entityManagerFactory': org.hibernate.exception.JDBCConnectionException: Unable to release JDBC Connection used for DDL execution
2023-01-04 09:23:03.267 INFO 40156 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2023-01-04 09:23:03.271 INFO 40156 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
BUILD SUCCESSFUL in 7s
4 actionable tasks: 1 executed, 3 up-to-date
AM 9:23:03: Execution finished ':test --tests "com.endofma.blog.controller.PostControllerTest.test5"'.
'Projects > blog' 카테고리의 다른 글
blog09: 페이징 처리(QueryDSL) 2 (0) | 2023.01.04 |
---|---|
blog09: 페이징 처리(QueryDSL) 1 (0) | 2023.01.04 |
blog07: 여러 글(list) 조회 (0) | 2023.01.02 |
blog06: 응답클래스 분리(req: PostCreate / res:PostResponse) (0) | 2022.12.30 |
blog05: 단건조회 (포스트 조회) (0) | 2022.12.29 |
Comments