코드로 배우는 스프링 웹 프로젝트 책을 정리한 내용입니다.
namespace와 id 속성 이름 동일하게 맞추기
public String ex02(@RequestParam("name") String name, @RequestParam("age") int age)
@GetMapping("/ex02List")
public String ex02List(@RequestParam("ids") ArrayList<String> ids)
배열의 경우도 동일하게 처리할 수 있다.
@GetMapping("/ex02List")
public String ex02List(@RequestParam("ids") String[] ids)
@Data
public class SampleDTOList {
private List<SampleDTO> list;
public SampleDTOList() {
list = new ArrayList<>();
}
}
@GetMapping("/ex02Bean")
public String ex02Bean(SampleDTOList list)
파라미터는 '[인덱스]'와 같은 형식으로 전달해 처리할 수 있다.
프로젝트 경로/sample/ex02Bean?list[0].name=aaa&list[2].name=bbb
톰캣 버전에 따라 위와 같은 문자열에서 '[]'문자를 특수문자로 허용하지 않을 수 있다. JS 사용하는 경우에는 encodeURIComponent()
로 해결가능
@InitBinder를 이용해서 날짜를 변환할 수도 있지만, 파라미터로 사용되는 인스턴스 변수에 @DateTimeFormat을 적용햐도 변환이 가능하다.
@Data
public String TodoDTO {
private String title;
@DateTimeFormat(pattern = "yyyy/mm/dd")
private Date dueDate;
}
프로젝트 경로?title=test&dueDate=2018/01/01
와 같이 형식만 맞다면 자동으로 날짜 타입으로 변환이 된다.
Java Beans 규칙에 맞는 객체는 다시 화면으로 객체를 전달한다. 기본생성자를 가져야 하며 getter/setter를 가진 클래스의 객체들을 의미한다.
반면 기본 자료형의 경우는 파라미터로 선언하더라도 기본적으로 화면까지 전달되지 않기때문에 @ModelAttribute("page") int page
를 써줘야 한다.
글을 등록했는데 사용자에게 글 등록이 잘 됐는지 확실히 확인시켜주고 싶을때 사용
redirect를 할때 파라미터에 넘겨줄 데이터가 있을때?
@RequiredArgsConstructor
@RequestMapping("/basic/items")
public class BasicItemController {
private final ItemRepository itemRepository;
@GetMapping("/{ItemId}")
public String item(@PathVariable long itemId, Model model) {
Item item = itemRepository.findById(itemId);
model.addAttribute("item", item);
return "basic/item";
}
@PostMapping("/add")
public String addItem(Item item, RedirectAttributes redirectAttributes) {
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getid());
redirectAttributes.addAttribute("status", "true");
return "redirect:/basic/items/{itemId}"; //위의 컨드롤러로 이동
}
}
itemId는 {itemId}로 들어가고 경로에 안 적힌 "status"는 파라미터로 들어간다. /basic/items/3?status=true
그래서 View단에서 파라미터의 status가 true일 경우 "저장 완료"를 표시하든가 하면 된다.
앗. 원래는 model에 담아서 전달해줘야 하는데 타임리프가 파라미터에서 바로 가져올 수 있도록 지원하기 때문에 저렇게 쓴다는데. jsp도 가능하대 ${param.name}
@GetMapping("/ex07")
public ResponseEntity<String> ex07() {
// {"name" : "홍길동"}
String msg = "{\"name\" : \"홍길동\"}";
HttpHeaders header = new HttpHeaders();
header.add("Content-Type", "application/json;charset=UTF-8");
return new ResponseEntity<>(msg, header, HttpStatus.OK);
}
requestHeader 에 accept-Encoding이 뭐지?
Accept-Encoding 요청 HTTP 헤더는, 보통 압축 알고리즘인, 클라이언트가 이해 가능한 컨텐츠 인코딩이 무엇인지를 알려줍니다. 컨텐츠 협상을 사용하여, 서버는 제안된 내용 중 하나를 선택하고 사용하며 Content-Encoding 응답 헤더를 이용해 선택된 것을 클라이언트에게 알려줍니다.
@PostMapping("/exUploadPost")
public void exUploadPost(ArrayList<MultipartFile> files) {
files.forEach(file -> {
log.info("---------------------------");
log.info("name: " + file.getOriginalFilename());
log.info("size: " + file.getSize());
});
<form action="/sample/exUploadPost" method="post" enctype="multipart/form-data">
<div>
<input type="file" name='files'>
</div>
<div>
<input type="file" name='files'>
</div>
<div>
<input type="file" name='files'>
</div>
<div>
<input type="file" name='files'>
</div>
<div>
<input type="file" name='files'>
</div>
<div>
<input type="submit">
</div>
</form>
최종 업로드를 하려면 byte[]를 처리해야 한대!! 이게 무슨뜻??
스프링 MVC에서는 이러한 작업을 다음과 같은 방식으로 처리할 수 있다.
4-1. @ExceptionHandler와 @ControllerAdvice를 이용한 처리
@ControllerAdvice는 AOP를 이용하며, 예외처리를 분리하는데 사용가능
CommonExceptionAdvice는 @ControllerAdvice 어노테이션을 적용하지만 예외 처리를 목적으로 생성하는 클래스이므로 별도의 로직을 처리하지는 않는다.
//CommonExceptionAdvice
@ControllerAdvice
@Log4j2
public class CommonExceptionAdvice {
@ExceptionHandler(Exception.class)
public String except(Exception ex, Model model) {
log.error("Exception ... {}", ex.getMessage());
model.addAttribute("exception", ex);
log.error(model);
return "error_page";
}
}
4-2. 404에러 처리
404에러는 가장 흔하게 사용되기에 조금 다르게 처리할 수도 있다.
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public String handle404(NoHandlerFoundException exception) {
return "custom404";
}
@SpringBootTest
@AutoConfigureMockMvc
class SampleControllerTest {
//애노테이션을 이용한 자동 주입 이외에도, 스프링 프레임워크에서 사용하는 것 처럼 MockMvc를 직접 생성하는 방법도 사용 가능합니다.
//직접 생성하게 되면 반복되는 코드를 설정할 수 있습니다. (예를들어 모든 예상 응답 status를 OK(200)으로 설정)
@Autowired
private MockMvc mockMvc;
@Test
void testList() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/board/list"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
@Test
void postTest() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/board/register")
.param("title", "테스트 새글 제목")
.param("content", "테스트 새글 내용")
.param("writer", "user00")
).andExpect(MockMvcResultMatchers.status().isOk());
}
새로운 게시물을 등록했을 때 한글 입력에 문제가 있다는 것을 발견했다면
1) 브라우저에서 한글이 깨져서 전송되는지 확인(F12)
2) 문제가 없다면 스프링 MVC 쪽에서 한글을 처리하는 필터를 등록해야 한다.(LOG를 통해 확인)
chain.do인가 그거 전후로 필터 전에 실행되어야 할것, 응답할 때 필터를 거치면서 실행되어야 할 것을 쓰면 된대.
어노테이션 | 기능 |
---|---|
@RestController | Controller가 REST 방식을 처리하기 위한 것임을 명시 |
@ResponseBodt | 일반적인 JSP와 같은 뷰로 전달되는 게 아니라 데이터 자체를 전달하기 위한 용도 |
@PathVariable | URL 경로에 있는 값을 파라미터로 추출하려고 할 때 사용 |
@CrossOrigin | Ajax의 코로스 도메인 문제를 해결해주는 어노테이션 |
@RequestBody | JSON 테이터를 원하는 타입으로 바인딩 처리 |
7-1) @RestController
메서트의 리턴 타입으로 사용자가 정의한 클래스 타입을 사용할 수 있고, 이를 JSON이나 XML로 자동으로 처리한다.
@GetMapping(value = "/getText", produces = "text/plain; charset=UTF-8")
public String getText() {
log.info("MIME TYPE: " + MediaType.TEXT_PLAIN_VALUE);
return "안녕하세요";
}
produces은 해당 메소드가 응답하는 MIME 타입. 메소드에 produces를 쓰면 클라이언트의 accept에 맞춰서 보낼 수 있는건가보다.(맞는지 알아봐야 함!) produces 속성은 반드시 지정해야 하는 것은 아니므로 생략하는 것도 가능하다.
객체의 반환
객체를 반환하는 작업은 JSON이나 XML을 이용한다.
SampleVO
List
Map<String, SampleVO>
ResponseEntity 타입
ResponseEntity는 테이터와 함께 HTTP 헤더의 상태 메시지 등을 같이 전달하는 용도로 사용한다. HTTP의 상태 코드와 에러 메시지 등을 함계 데이터를 전달할 수 있기 때문에 받는 입장에서는 확실하게 결과를 알 수 있다.
@GetMapping(value = "/check", params = {"height", "weight" })
public ResponseEntity<SampleVO> check(Double height, Double weight) {
SampleVO vo = new SampleVO(0, "" + height, "" + weight);
ResponseEntity<SampleVO> result = null;
if (height < 150) {
result = ResponseEntity.status(HttpStatus.BAD_GATEWAY).body(vo);
} else {
result = ResponseEntity.status(HttpStatus.OK).body(vo);
}
return result;
}
8-1) @PathVariable
REST 방식에서는 URL 내에 최대한 많은 정보를 담으려고 노력한다. @PathVariable 어노테이션을 이용해서 URL 상에 경로의 일부를 파라미터로 사용할 수 있다.
값을 얻을 떄에는 @PathVariable("pid") Integer pid
와 같이 기본 자료형이 아닌 Wapper클래스를 사용해야 한다.
**8-2) @RequestBody
클라이언트에서 post방식으로 JSON데이터를 보낼때 객체로 변환하기 위한 어노테이션이다.
MyBatis는 두 개 이상이 데이터를 파라미터로 전달하기 위해서는 1) 별도의 객체로 구성하거나, 2) Map을 이용하는 방식, 3) @Param을 이용해서 이름을 사용하는 방식이 있다.
여러 방식 중에 가장 간단하게 사용할 수 있는 방식이 @Param을 이용하는 방식이다.
@Param의 속성값은 MyBatis에서 SQL을 이용할 때 '#{}'의 이름으로 사용이 가능하다.
@Mapper
public interface ReplyMapper {
public List<ReplyVO> getListWithPaging(
@Param("cri") Criteria cri,
@Param("bno") Long bno);
}
XML로 처리할 때에는 지정된 'cri'와 'bno'를 모두 사용할 수 있다.
XML에서 '#{bno}'가 @Param("bno")와 매칭되어서 사용된다.
Mapper메소드를 호출할 때 파라미터로 전해줘서 사용하면 되겠다.
작업 | URL | HTTP 전송방식 |
---|---|---|
등록 | /replies/new | POST |
조회 | /replies/:rno | GET |
삭제 | /replies:/rno | DELETE |
수정 | /replies/:rno | PUT or PATCH |
페이지 | /replies/pages/:bno/:page | GET |
REST 방식으로 동작하는 URL을 설계할 때는 PK를 기준으로 작성하는 것이 좋다. PK만으로 조회, 수정, 삭제가 가능하기 때문이다. 다만 댓글의 목록은 PK를 사용할 수 없기 때문에 파라미터로 필요한 게시물의 번호(bno)와 페이지 번호(page) 정보들을 URL에서 표현하는 방식을 사용한다.
@PostMapping(value = "/new",
consumes = "application/json",
produces = {MediaType.TEXT_PLAIN_VALUE})
public ResponseEntity<String> create(@RequestBody ReplyVO vo) {
return insertCount == 1
? new ResponseEntity<>("success", HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
//BoardServiceImpl의 일부
@Override
public boolean modify(BoardVO board) {
return mapper.update(board) == 1;
}
정리하자면, 최근 스프링 부트는 JUnit 5를 사용하기 때문에 더이상 JUnit 4에서 제공하던 @RunWith를 쓸 필요가 없고 (쓰고 싶으면 쓸 수는 있지만), @ExtendWith를 사용해야 하지만, 이미 스프링 부트가 제공하는 모든 테스트용 애노테이션에 메타 애노테이션으로 적용되어 있기 때문에 @ExtendWith(SpringExtension.class)를 생략할 수 있다.
https://www.whiteship.me/springboot-no-more-runwith/
로그 라이브러리는 Logback, Log4J, Log4J2 등등 수 많은 라이브러리가 있는데, 그것을 통합해서 인터페이스로 제공하는 것이 바로 SLF4J 라이브러리다.
SLF4J는 인터페이스이고, 그 구현체로 Logback 같은 로그 라이브러리를 선택하면 된다.
//로그를 사용하지 않아도 a+b 계산 로직이 먼저 실행됨, 이런 방식으로 사용하면 X
log.debug("String concat log=" + name);
LEVEL: TRACE > DEBUG > INFO > WARN > ERROR
개발 서버는 debug 출력
운영 서버는 info 출력
이클립스 상에서 실행될 때는 "/" 경로로 설정되지 않고 "/contrller" 경로를 가지게 되므로 'Web Settings'를 이용해서 수정할 것
CSS가 작동하지 않는다면 경로가 어디로 설정되어 있는지 확인, resources아래 폴더에 css가 있는지 확인
includes를 사용할 경우 footer.jsp에서 jquery를 사용한다면 header에서 google을 통해 가져오도록 설정해야 한다???
html의 data 속성
"data-"를 통해 자바스크립트에서 처리할 수 있도록 할 수 있다.
<button data-oper='modify'>Modify</button>
라고 쓰면
자바스크립트에서 다음과 같이 쓸 수 있다.
$("button[data-oper='modify']").on("click", function(e) {
});
또는 "data-oper"값을 가져올 수 있다.
var operation = $(this).data("oper");
SelectKey는 주로 PK값을 미리(before) SQL을 통해서 처리해 두고 특정한 이름으로 결과를 보관하는 방식이다.
SELECT MAX(num) + 1 FROM board; 하면 구해진다!!!!!