스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 대시보드 - 인프런 | 강의 (inflearn.com)
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 인프런 | 강의
웹 애플리케이션 개발에 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. MVC 2편에서는 MVC 1편의 핵심 원리와 구조 위에 실무 웹 개발에 필요한 모든 활용 기술들을 학습할 수 있
www.inflearn.com
포맷터 - Formatter
컨버터의 특별한 버전
Converter: 범용 (객체 -> 객체)
Formatter: 문자에 특화 (객체 <-> 문자)
1000 -> 1,000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@Slf4j
public class MyNumberFormatter implements Formatter<Number> {
@Override
public Number parse(String text, Locale locale) throws ParseException {
log.info("text={}, locale={}", text, locale);
return NumberFormat.getInstance(locale).parse(text);
}
@Override
public String print(Number object, Locale locale) {
log.info("object={}, locale={}", object, locale);
return NumberFormat.getInstance(locale).format(object);
}
}
|
cs |
포맷터 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class MyNumberFormatterTest {
MyNumberFormatter formatter = new MyNumberFormatter();
@Test
void parse() throws ParseException {
Number result = formatter.parse("1,000", Locale.KOREA);
assertThat(result).isEqualTo(1000L);
}
@Test
void print() {
String result = formatter.print(1000, Locale.KOREA);
assertThat(result).isEqualTo("1,000");
}
}
|
cs |
포맷터를 지원하는 컨버전 서비스
DefaultFormattingConversionService
FormattingConversionService에 기본적인 통화, 숫자 관련 몇 가지 기본 포맷터 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
@Test
void formattingConversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
// 컨버터 등록
conversionService.addConverter(new StringToIpPortConverter());
conversionService.addConverter(new IpPortToStringConverter());
// 포맷터 등록
conversionService.addFormatter(new MyNumberFormatter());
// 컨버터 사용
IpPort ipPort = conversionService.convert("127.0.0.1:8080", IpPort.class);
assertThat(ipPort).isEqualTo(new IpPort("127.0.0.1", 8080));
// 포맷터 사용
assertThat(conversionService.convert("1,000", Long.class)).isEqualTo(1000L);
assertThat(conversionService.convert(1000, String.class)).isEqualTo("1,000");
}
|
cs |
포맷터 적용하기
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
// 컨버터 추가
registry.addConverter(new StringToIpPortConverter());
registry.addConverter(new IpPortToStringConverter());
// 포맷터 추가
registry.addFormatter(new MyNumberFormatter());
}
}
|
cs |
결과
- ${number}: 10000
- ${{number}}: 10,000
- ${ipPort}: hello.typeconverter.type.IpPort@59cb0946
- ${{ipPort}}: 127.0.0.1:8080
스프링이 제공하는 기본 포맷터
애토네이션 기반으로 원하는 형식 지정
@NumberFormat: 숫자 관련 형식 지정 포맷터
@DateTimeFormat: 날짜 관련 형식 지정 포맷터
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
|
@Controller
public class FormatterController {
@GetMapping("/formatter/edit")
public String formatterForm(Model model) {
Form form = new Form();
form.setNumber(10000);
form.setLocalDateTime(LocalDateTime.now());
model.addAttribute("form", form);
return "formatter-form";
}
@PostMapping("/formatter/edit")
public String farmatterEdit(@ModelAttribute Form form) {
return "formatter-view";
}
@Data
static class Form{
@NumberFormat(pattern = "###,###")
private Integer number;
@DateTimeFormat(pattern = "yyyy-mm-dd HH:mm:ss")
private LocalDateTime localDateTime;
}
}
|
cs |
결과
• ${form.number}: 10000
• ${{form.number}}: 10,000
• ${form.localDateTime}: 2021-01-01T00:00:00
• ${{form.localDateTime}}: 2021-01-01 00:00:00
정리
컨버터, 포맷터 등록 방법 다르지만,
사용할 때는 conversionService를 통해서 일관성 있게 사용 가능
주의!
메시지 컨버터( HttpMessageConverter )에는 컨버전 서비스가 적용되지 않는다.
특히 객체를 JSON으로 변환할 때 메시지 컨버터를 사용하면서 이 부분을 많이 오해하는데,
HttpMessageConverter 의 역할은 HTTP 메시지 바디의 내용을 객체로 변환하거나 객체를 HTTP 메시지
바디에 입력하는 것이다. 예를 들어서 JSON을 객체로 변환하는 메시지 컨버터는 내부에서 Jackson 같은
라이브러리를 사용한다. 객체를 JSON으로 변환한다면 그 결과는 이 라이브러리에 달린 것이다. 따라서
JSON 결과로 만들어지는 숫자나 날짜 포맷을 변경하고 싶으면 해당 라이브러리가 제공하는 설정을 통해서
포맷을 지정해야 한다. 결과적으로 이것은 컨버전 서비스와 전혀 관계가 없다.
컨버전 서비스는 @RequestParam , @ModelAttribute , @PathVariable , 뷰 템플릿 등에서 사용할 수
있다.
잭슨 라이브러리 자체에서 제공하는 데이터 포맷터 사용해야 함
파일 업로드
문자, 바이너리 동시 전송
-> enctype="multipart/form-data"
boundary
업로드 파일 제한
파일 하나: spring.servlet.multipart.max-file-size=1MB
파일 전체 합: spring.servlet.multipart.max-request-size=10MB
멀티파트 리졸버는 멀티파트 요청인 경우 서블릿 컨테이너가 전달하는 일반적인 HttpServletRequest 를
MultipartHttpServletRequest 로 변환해서 반환한다.
> MultipartHttpServletRequest 는 HttpServletRequest 의 자식 인터페이스이고, 멀티파트와 관련된
추가 기능을 제공한다.
서블릿과 파일 업로드2
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
@Slf4j
@Controller
@RequestMapping("/servlet/v2")
public class ServletUploadControllerV2 {
@Value("${file.dir}")
public String fileDir;
@GetMapping("/upload")
public String newFile() {
return "upload-form";
}
@PostMapping("/upload")
public String saveFileV1(HttpServletRequest request) throws IOException, ServletException {
log.info("request={}", request);
String itemName = request.getParameter("itemName");
log.info("itemName={}", itemName);
Collection<Part> parts = request.getParts();
log.info("parts={}", parts);
for (Part part : parts) {
log.info("==== PART ====");
log.info("name={}", part.getName());
Collection<String> headerNames = part.getHeaderNames();
for (String headerName : headerNames) {
log.info("header {}: {}", headerName, part.getHeader(headerName));
}
// 편의 메서드
log.info("submittedFilename={}", part.getSubmittedFileName());
log.info("size={}", part.getSize());
// 데이터 읽기
InputStream inputStream = part.getInputStream();
String body = StreamUtils.copyToString(inputStream, UTF_8);
log.info("body={}", body);
// 파일 저장
if (StringUtils.hasText(part.getSubmittedFileName())) {
String fullPath = fileDir + part.getSubmittedFileName();
log.info("파일 저장 fullPath={}", fullPath);
part.write(fullPath);
}
}
return "upload-form";
}
}
|
cs |
스프링과 파일 업로드
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
29
30
|
@Slf4j
@Controller
@RequestMapping("/spring")
public class SpringUploadController {
@Value("${file.dir}")
public String fileDir;
@GetMapping("/upload")
public String newFile() {
return "upload-form";
}
@PostMapping("/upload")
public String saveFile(@RequestParam String itemName,
@RequestParam MultipartFile file,
HttpServletRequest request) throws IOException {
log.info("request={}", request);
log.info("itemName={}", itemName);
log.info("multipartFile={}", file);
if (!file.isEmpty()) {
String fullPath = fileDir + file.getOriginalFilename();
log.info("파일 저장 fullPath={}", fullPath);
file.transferTo(new File(fullPath));
}
return "upload-form";
}
}
|
cs |
@RequestParam MultipartFile file
업로드하는 HTML Form의 name에 맞추어 @RequestParam 을 적용하면 된다. 추가로
@ModelAttribute 에서도 MultipartFile 을 동일하게 사용할 수 있다.
MultipartFile 주요 메서드
file.getOriginalFilename() : 업로드 파일 명
file.transferTo(...) : 파일 저장
'오늘 배운 것' 카테고리의 다른 글
Docker, Kubernetes, DevOps의 개념 (0) | 2021.08.17 |
---|---|
[스프링 핵심 원리 기본] 객체지향 설계와 스프링 (0) | 2021.07.28 |
[스프링 MVC 2] API 예외 처리(2), 스프링 타입 컨버터 (0) | 2021.07.26 |
[스프링 MVC 2] API 예외 처리 (0) | 2021.07.24 |
[스프링 MVC 2] 예외 처리와 오류 페이지 (0) | 2021.07.23 |