import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.Period;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import java.util.Locale;

/**
 * 월 달력 생성기
 *
 * @author
 */
public class Test {

	public static void main(String[] args) {

		final int addMonth = 2; //2개월 후 데이터 생성

		DateTimeFormatter fmtMonth = DateTimeFormat.forPattern("yyyyMM");
		DateTimeFormatter fmtDay = DateTimeFormat.forPattern("yyyyMMdd");

		//대상 월
		DateTime now = DateTime.now();
		String strTargetMonth = now.plus(Period.months(addMonth)).toString(fmtMonth);

		DateTime dt = DateTime.parse(strTargetMonth, fmtMonth);

		int startDay = dt.dayOfMonth().getMinimumValue();
		int endDay = dt.dayOfMonth().getMaximumValue();

		String strDay;
		String dayOfWeek;
		//boolean isHoliDay = false;
		for (int i = startDay; i <= endDay; i++) {
			strDay = strTargetMonth + StringUtils.leftPad(Integer.toString(i), 2, "0");
			dayOfWeek = DateTime.parse(strDay, fmtDay).dayOfWeek().getAsShortText(Locale.KOREA);

			System.out.println(String.format("%s | %s", strDay, dayOfWeek));

		}

	}
}

Spring에서 예외 핸들링 중 특정 예외에서 어떤 경우에는 json으로 응답하고 어떤 경우에는 유저에게 에러 페이지를 보여줘야하는 경우가 존재합니다.

이런 경우 간단히 처리하는 예제 소스입니다.(더 좋은 방법이 있을 수 있지만 고민이 필요)

 

 

/**
	 * 최상위 예외(Exception) 처리
	 * - 에러로그를 남겨서 담당자에게 이메일 발송(logback에 설정 됨)하여 알 수 있도록 하기 위함
	 * - 유저에게 에러 페이지를 보여주기 위해서 ModelAndViewDefiningException를 사용(DispatcherServlet에서 view 처리됨)
	 *
	 * @param ex
	 * @return
	 */
	@ExceptionHandler(Exception.class)
	public ResponseEntity<Object> RootException(Exception ex) throws ModelAndViewDefiningException {

		errorLog("RootException", ex);
		log.debug("exception class ===> {}", ex.getClass().getSimpleName());

		if (CommonUtil.isAjaxReq(HttpRequestUtil.getCurrentRequest())) { //ajax라면 json으로 응답
			RtnVO rtnVO = new RtnVO();
			rtnVO.setErrorCd(ErrorCd.SYSTEM_ERROR.name());
			rtnVO.setMsg("서버 에러가 발생했습니다. 담당자에게 문의하세요");
			return new ResponseEntity<>(rtnVO, ErrorCd.SYSTEM_ERROR.getStatus());
		}

		ModelAndView mav = new ModelAndView();
		mav.setViewName("forward:/error/serverError"); //ajax 요청이 아니라면 유저에게 에러메시지 페이지를 보여줌(/error/serverError는 에러 페이지 랜더링용 공용 URL) 
		throw new ModelAndViewDefiningException(mav);
	}

참고로 DispatcherServlet.java에서 ModelAndViewDefiningException 처리가 되어있다.

 

Java의 java.time.LocalDateTime관련 자주 사용하는 몇 가지를 메모 목적으로 정리합니다.

  • 예전 java 1.7까지는 joda를 많이 사용했고, Java기본 패키지에 포함된 이후에도 바쁘면 joda를 사용했습니다.
  • 가능하면 java 기본 패키지를 사용하는게 좋습니다.
  • 계속해서 내용은 추가할 예정입니다.

 

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Date;

/**
 * Java의 java.time.LocalDateTime 관련 몇가지 사용 예 - 작성중
 * - joda를 많이 썻던 개발자들을 위해 샘플 작성
 *
 * @author
 */
public class LocalDateTimeSample {

	public static ZoneId pstZoneId = ZoneId.of("America/Los_Angeles"); //PST는 ZoneId가 'America/Los_Angeles' 임
	public static ZoneId pdtZoneId = ZoneId.of("GMT-07:00"); //PDT는 ZoneId가 'GMT-07:00' 임. 참고: https://savvytime.com/converter/pdt-to-kst-utc/aug-1-2021/3am

	public static DateTimeFormatter FMT_YMDHIS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

	/**
	 * Java의 java.time.LocalDateTime 관련 몇가지 샘플
	 *
	 * @param args
	 */
	public static void main(String[] args) {

		System.out.println("\n");

		final Date now = new Date();

		System.out.println(String.format("java.util.Date 타입의 현재 일시: %s\n\n", now));

		LocalDateTime ldt = LocalDateTime.ofInstant(now.toInstant(), ZoneId.systemDefault());
		System.out.println(String.format("java.util.Date를 java.time.LocalDateTime 로 변환:%s\n", ldt));

		//혹은 아래와 유사하하게 Date객체로 변환 가능
		//Instant instant = utcZonedDateTime.withZoneSameInstant(ZoneId.of("UTC")).toLocalDateTime().atZone(ZoneId.of("UTC")).toInstant();
		//java.util.Date.from(instant);

		final String pattern = "yyyy-MM-dd-HH:mm";
		System.out.println(String.format("LocalDateTime을 패턴 '%s'으로 포맷팅: %s\n", pattern, ldt.format(DateTimeFormatter.ofPattern(pattern))));

		System.out.println(String.format("1일 후는: %s", ldt.plusDays(1).format(FMT_YMDHIS)));
		System.out.println(String.format("3일전은: %s", ldt.minusDays(3).format(FMT_YMDHIS)));

		String ymd = "20220501";

		DateTimeFormatter fmtYmd = DateTimeFormatter.ofPattern("yyyyMMdd");
		LocalDate dateYmd = LocalDate.parse(ymd, fmtYmd);
		System.out.println(String.format("%s 문자열을 포맷팅 후 date객체 변환 결과: %s", ymd, dateYmd));

		LocalDate toDay = LocalDate.now();
		System.out.println(String.format("오늘은 '%s' 입니다.", toDay));
		System.out.println(String.format("이번달의 1일은 '%s' 입니다.", toDay.withDayOfMonth(1)));

		System.out.println("\n");
		ZoneId defaultZoneId = ZoneId.systemDefault();
		Date firstMonthDayDT = Date.from(toDay.atStartOfDay(defaultZoneId).toInstant());
		System.out.println(String.format("이번달의 1일의 java date타입은 '%s' 입니다.", firstMonthDayDT));

		System.out.println("\n");
		System.out.println("원본 LocalDateTime: " + ldt);
		System.out.println("\t초 미만 절삭: " + ldt.truncatedTo(ChronoUnit.SECONDS));
		System.out.println("\t분 미만 절삭: " + ldt.truncatedTo(ChronoUnit.MINUTES));
		System.out.println("\t시간 미만 절삭: " + ldt.truncatedTo(ChronoUnit.HOURS));
		System.out.println("\t일 미만 절삭: " + ldt.truncatedTo(ChronoUnit.DAYS));
	}
}

 

결과

코드 실행 결과

 

1. 쿼리 부분 예

<!-- 해당 유저ID가 존재하는지 체크(존재시 1 리턴으로 boolean 체크됨) -->
<select id="isExistUser" parameterType="String" resultType="boolean">
    /* user.isExistUser */
    SELECT
    EXISTS
    (SELECT 1 FROM user WHERE user_id = #{userId} LIMIT 1) #LIMIT 1은 없어도 무방하지만 방어차원에서
</select>



2. java부분 예(DAO영역)

public boolean isExistUser(String userId) {
    return sqlSession.selectOne(NAMESPACE + "isExistUser", userId);
}

Spring valid 자주 사용하는 내용/샘플 정리

 - 필요시 복붙 목적으로 메모해둔 글인점을 감안해주세요

 

//${validatedValue}를 이용하면 검증대상 값을 가져올 수 있음
@NotBlank(message = "'authKey' cannot be null or empty")
@Size(max = 50, message = "The 'authKey' must be a maximum of {max}. But the request is [${validatedValue}].")
private String authKey;
  1. 신규 개발자 입사 또는 성능 문제 발생시 공유하는 java 코딩 방법
  2.  모두 지킬 수는 없지만 알고 있으면 좋은 내용


p.s. 생각날때 개인 의견 추가 및 링크들 추가 예정

git 링크

 - https://github.com/oshnew/study-modern-java/blob/master/src/main/java/stream/StreamSorted.java

 

import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 내부 개발자 교육을 위한 간단한 java Stream 샘플 소스
 *  - 정렬
 *
 * @author
 */
public class StreamSorted {

	public static void main(String[] args) {

		//테스트 데이터 셋팅
		List<StatVO> statList = Arrays.asList(new StatVO("a", 3), new StatVO("b_a", 2), new StatVO("b_c", 1), new StatVO("d", 7));

		System.out.println("=== cnt 오름차순 정렬");
		statList.stream().sorted(Comparator.comparing(StatVO::getCnt)).forEach(System.out::println);

		System.out.println("\n\n");
		System.out.println("=== cnt 내림차순 정렬");
		statList.stream().sorted(Comparator.comparing(StatVO::getCnt).reversed()).forEach(System.out::println);

		System.out.println("\n\n");
		System.out.println("=== stat name 오름차순 후에 cnt 내림차순정렬"); //다수 필드 정렬
		statList.stream().sorted(Comparator.comparing(StatVO::getStat).thenComparing(StatVO::getCnt).reversed()).forEach(System.out::println);

		System.out.println("\n\n");
		List<StatVO> descList = statList.stream().sorted(Comparator.comparing(StatVO::getCnt)).collect(Collectors.toList());
		System.out.println("=== 오름차순 정렬하여 collect한 리스트");
		descList.stream().forEach(System.out::println);

	}

	@Data
	@AllArgsConstructor
	public static class StatVO {
		private String stat;
		private int cnt;
	}

}

1. 목적/배경

 - 가끔 대용량 데이터 처리(예. DB에 100만건 insert values로 bulk insert할때) 부하 분산을 위해 적절한 데이터 사이즈로 분할하여 처리할 필요가 있음

   (간단한 수학 계산인데 필요할때마다 다시 코딩하는게 번거롭고 헷갈려서 메모 목적으로 작성해둡니다.

 

2. 처리 방법

 - 적절한 데이터 사이즈만큼 잘라서 loop처리

 

 

샘플 소스

/**
	 * N개의 데이터 리스트를 자르는 간단한 소스
	 *   - 대용량 DB 저장 등의 기능 구현때 부하 분산 목적
	 *
	 * @param args
	 */
	public static void main(String[] args) {

		//테스트 데이터 셋팅
		int dataSize = 103;
		ArrayList<Integer> dataList = new ArrayList<>(dataSize);
		for (int i = 1; i <= dataSize; i++) {
			dataList.add(i);
		}

		//처리 시작
		final int dataCnt = dataList.size();
		final int PER_CNT = 10; //1회 처리 건수

		int fromIndex = 0;
		int toIndex = 0;

		boolean isNeedNextLoop = true;
		int loopCompleteCnt = 0; //loop처리 완료된 횟수
		do {

			fromIndex = loopCompleteCnt * PER_CNT;  //최초 시작은 0, 이후 PER_SAVE_CNT개 만큼씩 fromIndex는 증가
			toIndex = fromIndex + PER_CNT; //from index에서 1회 최대 건수만큼까지 toIndex는 증가
			if (toIndex >= dataCnt) { //증가한 toIndex가 데이터 전체 갯수보다 클때는 toIndex 값을 변경(마지막 loop에서는 데이터 element갯수가 1회 처리량보다 적거나 같음)
				toIndex = dataCnt;
				isNeedNextLoop = false;
			}		

			//비즈니스 로직 작성
			System.out.println(String.format("%s회 data subList: %s", loopCompleteCnt+1, dataList.subList(fromIndex, toIndex)));
            loopCompleteCnt++;

		} while (isNeedNextLoop);

	}

 

샘플소스 결과 

1회 data subList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2회 data subList: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
3회 data subList: [21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
4회 data subList: [31, 32, 33, 34, 35, 36, 37, 38, 39, 40]
5회 data subList: [41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
6회 data subList: [51, 52, 53, 54, 55, 56, 57, 58, 59, 60]
7회 data subList: [61, 62, 63, 64, 65, 66, 67, 68, 69, 70]
8회 data subList: [71, 72, 73, 74, 75, 76, 77, 78, 79, 80]
9회 data subList: [81, 82, 83, 84, 85, 86, 87, 88, 89, 90]
10회 data subList: [91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
11회 data subList: [101, 102, 103]

+ Recent posts