[무료] 자바 스프링부트 활용 웹개발 실무용 - 인프런 | 강의
실무자가 사용하는 자바 웹개발 Spring Boot, Vue.JS 기초 설정 ~ 공통 설계까지 간단하게 동영상보고 해보며 배우기, - 강의 소개 | 인프런...
www.inflearn.com
(본 포스팅은 위의 인프런 강의를 기반으로 작성했습니다.)
준비물
- IDE : Eclipse 2021-09 버전
- Java : openjdk11
'ControllerAdvice 사용과 예외처리 다국어 활용'
(1) WebConfiguration 클래스 생성 + messageSource
@Bean
public ReloadableResourceBundleMessageSource messageSource() {
ReloadableResourceBundleMessageSource source = new
ReloadableResourceBundleMessageSource();
source.setBasename("classpath:/messages/message");
source.setDefaultEncoding("UTF-8");
source.setCacheSeconds(60);
source.setDefaultLocale(Locale.KOREAN);
source.setUseCodeAsDefaultMessage(true);
return source;
}
(2) 패키지 생성
kr.co.songjava.configuration.exception
kr.co.songjava.configuration.web.bind.annotation
(3) AbstractBaseException 클래스 생성
private static final long serialVersionUID = 8342235231880246631L;
protected BaseResponseCode responseCode;
protected Object[] args;
public AbstractBaseException() {
}
public AbstractBaseException(BaseResponseCode responseCode) {
this.responseCode = responseCode;
}
public BaseResponseCode getResponseCode() {
return responseCode;
}
public Object[] getArgs() {
return args;
}
(3) BaseException 클래스 생성
private static final long serialVersionUID = 8342235231880246631L;
public BaseException() {
}
public BaseException(ResponseCode responseCode) {
this.responseCode = responseCode;
}
(4) BaseControllerAdvice 클래스 생성
@Autowired
private MessageSource messageSource;
@ExceptionHandler(value = { BaseException.class })
@ResponseStatus(HttpStatus.OK)
@ResponseBody
private BaseResponse<?> handleBaseException(BaseException e, WebRequest request) {
return new BaseResponse<?>(e.responseCode(), messageSource.getMessage(e.responseCode());
}
'Interceptor와 Logback 사용법'
(1) BaseHandlerInterceptor 추가
Logger logger = LoggerFactory.getLogger(getClass());
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("preHandle requestURI : {}", request.getRequestURI());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
logger.info("postHandle requestURI : {}", request.getRequestURI());
}
(2) WebConfiguration 클래스에 WebMvcConfigurer 인터페이스 구현
@Bean
public BaseHandlerInterceptor baseHandlerInterceptor() {
return new BaseHandlerInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(baseHandlerInterceptor());
}
(3) application.properties 설정
logging.config = classpath:logback/logback-default.xml
(4) logback-default.xml 추가
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>${LOG_PATTERN}</Pattern>
</layout>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
(5) 라이브러리 사용 및 SQL 로그 보기 -> 로그 줄이는 방법
<logger name="org.springframework.boot">
<level value="ERROR"/>
</logger>
<logger name="org.springframework.boot.devtools.restart">
<level value="ERROR"/>
</logger>
<logger name="org.springframework">
<level value="ERROR"/>
</logger>
<logger name="springfox">
<level value="ERROR"/>
</logger>
<logger name="org.hibernate">
<level value="ERROR"/>
</logger>
<logger name="org.apache">
<level value="ERROR"/>
</logger>
<logger name="org.apache.http.impl.conn">
<level value="ERROR"/>
</logger>
<logger name="log4jdbc">
<level value="ERROR"/>
</logger>
<logger name="org.mybatis">
<level value="ERROR"/>
</logger>
<logger name="com.zaxxer">
<level value="ERROR"/>
</logger>
<!-- Query Loggers -->
<logger name="jdbc" level="ERROR"/>
<logger name="jdbc.connection" level="ERROR"/>
<!-- SQL문만을 로그로 남기며, PreparedStatement일 경우 관련된 argument 값으로 대체된 SQL문이 보여진다. -->
<logger name="jdbc.sqlonly" level="ERROR"/>
<!-- SQL문과 해당 SQL을 실행시키는데 수행된 시간 정보(milliseconds)를 포함한다. -->
<logger name="jdbc.sqltiming" level="ERROR"/>
<!-- ResultSet을 포함한 모든 JDBC 호출 정보를 로그로 남기므로 매우 방대한 양의 로그가 생성된다. -->
<logger name="jdbc.resultset" level="OFF"/>
<logger name="jdbc.resultsettable" level="ERROR"/>
<!-- ResultSet을 제외한 모든 JDBC 호출 정보를 로그로 남긴다. 많은 양의 로그가 생성되므로 특별히 JDBC 문제를 추적해야 할 필요가 있는 경우를 제외하고는 사용을 권장하지 않는다. -->
<logger name="jdbc.audit" level="OFF"/>
LOG 파일로 남기기
/app/logs/example-spring.log true 100MB
/app/logs/example-spring/logs/%d{yyyy-MM-dd}/backup.%i.zip 100MB 30 ${LOG_PATTERN}
'Mybatis와 Mysql 환경에서 대용량 빠르게 등록'
@ApiOperation(value = "대용량 등록처리1", notes = "대용량 등록처리1")
@PutMapping("/saveList1")
public BaseResponse<Boolean> saveList1() {
int count = 0;
// 테스트를 위한 랜덤 1만건의 데이터를 생성
List<BoardParameter> list = new ArrayList<BoardParameter>();
while (true) {
count++;
String title = RandomStringUtils.randomAlphabetic(10);
String contents = RandomStringUtils.randomAlphabetic(10);
list.add(new BoardParameter(title, contents));
if (count >= 10000) {
break;
}
}
//시간 측정
long start = System.currentTimeMillis();
boardService.saveList1(list);
long end = System.currentTimeMillis();
logger.info("실행 시간 : {}", (end - start) / 1000.0);
return new BaseResponse<Boolean>(true);
}
@PutMapping("/saveList2")
@ApiOperation(value = "대용량 등록처리2", notes = "대용량 등록처리2")
public BaseResponse<Boolean> saveList2() {
int count = 0;
// 테스트를 위한 랜덤 1만건의 데이터를 생성
List<BoardParameter> list = new ArrayList<BoardParameter>();
while (true) {
count++;
String title = RandomStringUtils.randomAlphabetic(10);
String contents = RandomStringUtils.randomAlphabetic(10);
list.add(new BoardParameter(title, contents));
if (count >= 10000) {
break;
}
}
//시간 측정
long start = System.currentTimeMillis();
boardService.saveList2(list);
long end = System.currentTimeMillis();
logger.info("실행 시간 : {}", (end - start) / 1000.0);
return new BaseResponse<Boolean>(true);
}
public void saveList1(List<BoardParameter> list) {
//1개가 생성될때마다 레파지토리 호출 =connection매번 호출
for (BoardParameter parameter : list) {
repository.save(parameter);
}
}
/**
* 100씩 배열에 담아서 일괄 등록 처리.
*/
public void saveList2(List<BoardParameter> boardList) {
Map<String, Object> paramMap = new HashMap<String, Object>();
//map에 리스트(1만건) 한번에 담아서 총 connection 1번 호출
paramMap.put("boardList", boardList);
repository.saveList(paramMap);
}
- pom.xml에 commons-lang3 라이브러리 추가 (테스트 데이터 랜덤 문자열 생성에 사용)
- application.properties jdbcUrl에 옵션 추가
allowMultiQueries=true
- logback-default.xml에 root level DEBUG로 변경
- Board.xml에 saveList 쿼리 추가 -> foreach 사용하여 배열 INSERT QUERY 사용
- BoardRepository에 saveList 관련 기능 추가
- BoardController, BoardService saveList1, saveList2 10000건 데이터 생성
- saveList1, saveList2 속도 테스트
'Mybatis와 enum 활용 DB 컬럼 in out 사용방법'
(1) Board 테이블에 BOARD_TYPE VARCHAR(10) 컬럼 추가, Board.xml 쿼리 수정
ALTER TABLE
T_BOARD
CHANGE COLUMN
BOARD_SEQ
BOARD_SEQ
INT(11) NOT NULL AUTO_INCREMENT COMMENT '게시물 번호' FIRST, ADD COLUMN
BOARD_TYPE
CHAR(5) NOT NULL COMMENT '게시판 종류' AFTER
BOARD_SEQ
;
(2) BaseCodeLabelEnum, BoardType enum class 생성
(3) BardParameter BoardType boardType 변수 추가
(4) Board BoardType boardType 변수 추가
(5) BaseCodeLabelEnumJsonSerializer 클래스 추가
(6) WebConfiguration ObjectMapper, MappingJackson2JsonView Bean 등록
(7) Swagger에서 save 호출
(8) Swagger에서 list 조회 결과 확인
대부분은 테이블에 공통코드 테이블을 만들고 코드, 코드명 형태로 관리한다. 관리자에서 CRUD 형태 또는 개발자가 필요코드만 INSERT하여 관리하는 방식 등 다양하다.
+Mybatis TypeHandler 및 코드규칙
- 자바코드와 분리 및 DB 오류 방지를 위해 TypeHandler를 통해 사용한다.
- 현재 영상은 TypeHandler를 관리하지 않고, JAVA ENUM과 DB 필드가 1:1 연결되는 방식이다.
- VARCHAR 가변을 사용하고 싶지 않다면, CHAR(5) 고정 자리 수 형태로 해서 BT001, BT002, BT003 코드 형태로 가능하다.
장점 1. 불필요한 테이블 및 코드 관리가 필요없다. 2. 리스트, 상세 조회시 코드에 대한 코드명을 TABLE로 JOIN하거나 FUNCTION을 가져오는 불필요한 쿼리가 없어진다. 3. 코드, 코드명 추가/수정/삭제 시 운영 서버에 배포한다면 DB를 직접 건드리지 않고, 자동으로 적용된다. 4. int, String으로 선언한다면 코드에 대해 공백, 자리 수, 유효한 값인지 체크 로직이 들어가야 한다. enum은 안해도 된다. (Enum에 해당하지 않는 값이 들어온다면 Spring 에서 에러가 발생한다!!!!!) |
단점 1. 사이트 오픈 후 코드를 추가, 수정, 삭제가 불가능하다. 2. 코드 추가/수정/삭제는 개발자가 직접 코드를 수정하고 운영 서버에 배포하고, 재기동을 해야 적용된다. 3. DBMS TOOL에서 직접 INSERT, UPDATE 등을 통하여 잘못된 ENUM 정의되지 않은 코드가 있는 경우 오류가 발생한다. |
참조
stylehosting/example-spring: Example Spring Boot Project (github.com)
'Spring boot' 카테고리의 다른 글
2# 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 (0) | 2023.02.03 |
---|---|
1# 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 (0) | 2023.01.26 |