안녕하세요 - 티친님들🙋♀️
저번 포스팅을 이어서 작성해 보겠습니다.
부족한 신입 개발자 이지만 한번 봐주시겠습니까 -? 👩🏻💻
이전 포스팅👇
개발 테스트 준비
필요한 데이터
1. 현재 서버의 날짜 : today
2. 입력받은 날짜 데이터 2개(시작일 : startDate , 종료일: endDate)
3. 현재 서버의 날짜(today) - 35일 , 조회 시작 가능한 날짜 : maxStartDate
검증을 해야 하는 부분
현재 날짜로부터 최대 5주 이전부터 현재까지의 데이터를 가져와야 하므로 최대 5주(35일) 이내의 데이터를 요구하는지 검증
검증을 위한 예상 처리로직
1. startDate가 maxStartDate보다 이전이면 조회 시작 날짜를 maxStartDate로 설정
2. startDate가 today보다 이후라면 조회 시작 날짜를 maxStartDate 설정
3. startDate가 maxStartDate 이후라면 startDate 값은 그대로 유지
4. endDate와 today가 동일하면 endDate를 today로 설정
5. endDate와 today를 비교하여 today보다 이전이라면 endData 값은 그대로 유지
6. endDate와 today를 비교하여 today보다 이후라면 endDate를 today로 설정
7. startDate와 endDate가 정상범주라면 그대로 유지
저번 포스팅에는 테스트코드를 작성 해 필요한 데이터 목록 중 1번째 항목을 준비해 봤다.
이제 2번째 날짜를 준비하면서 String ➡️ Date , Date ➡️ String으로 변환을 하는 메서드를 미리 만들어 두었다.
2번째 입력받은 날짜 정보는 클라이언트에서 전달한 request를 가지고 Map으로 받아 parameter를 매핑한다.
매핑된 parameter를 서비스단에 전달해 조회하고 페이지를 만들어 view단으로 전달하는데
그 사이에 startDate와 endDate를 가져와 검증하고 올바른 데이터를 전달하는 로직을 만들어야 했다.
오늘은 2번째 데이터와 3번째 데이터를 만들고 검증까지 진행해 보려 한다.
혹시라도 이전 포스팅에 부분적인 내용과 테스트 코드를 보려면 접은 글 클릭!👇
날짜 계산이 필요하게 된 이유
업무를 하면서 기존 서비스 중인 사이트에 보안 개선건으로 요청이 왔다.
해당 기능은 날짜와 검색할 조건을 선택하고 🔍검색 버튼을 클릭하게 되면 해당 조건이 일치한 데이터를 DB에서 가져와
화면에 보여주는 기능이었다.
사용자의 이력을 조회하는 기능인데 보안 이슈로 받은 메일 사항은 이러했다.
클라이언트에서 날짜 선택이 가능한 날짜는 당일로부터 35일 전부터 당일까지 이지만
사용자가 직접 Parameter로 입력해 전송 시(-35일 이전보다 더 이전 일정으로 선택) 검색이 되어 response 된다는 것이었다.
Parameter조작으로 인한 보안 취약점이 있으니 개선하라는 메일이었다.
테스트코드
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test_DateCalCode {
public static void main(String[] args) throws ParseException {
Date today = new Date();
System.out.println("오늘의 날짜 입니다 : " + today);
SimpleDateFormat numberDateFormat = new SimpleDateFormat("yyyyMMdd");
SimpleDateFormat dashDateFormat = new SimpleDateFormat("yyyy-MM-dd");
System.out.println("------------------------------");
System.out.println("today : " + numberDateFormat.format(today));
System.out.println("today : " + dashDateFormat.format(today));
System.out.println("------------------------------");
}
}
포맷형식을 지정했을 때 이렇게 출력이 된다.
날짜 포맷을 지정하는 것과 자료형을 변환하는 것은 자주 사용할 것 같아 메서드를 만들어 분리해 준비했다.
String To Date , Date To String
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test_DateCalCode {
public static void main(String[] args) throws ParseException {
String startDate = "2024-07-20";
String endDate = "2024-08-10";
Date today = new Date(); // 오늘 날짜
// String -> Date로 객체를 변환 ,
Date startDateTo = strToDate(startDate);
Date endDateTo = strToDate(endDate);
System.out.println("=============================================================");
System.out.println(" 생성한 Date 객체 , today : " + today);
System.out.println(" 포멧팅 String -> Date , startDate : " + startDateTo);
System.out.println(" 포멧팅 String -> Date , endDate : " + endDateTo);
System.out.println("=============================================================");
// Date -> String으로 변환
System.out.println(" 파싱 Date -> String , today : " + dateToStr(today));
System.out.println(" 파싱 Date -> String , startDateTo : " + dateToStr(startDateTo));
System.out.println(" 파싱 Date -> String , endDateTo : " + dateToStr(endDateTo));
}
// Date 포멧 지정 후 String으로 리턴 함
private static String dateToStr(Date paramDate) throws ParseException {
SimpleDateFormat dashDateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dashDateFormat.format(paramDate);
}
// String 에서 Date로 리턴
private static Date strToDate(String strDate) throws ParseException {
SimpleDateFormat dashDateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dashDateFormat.parse(strDate);
}
}
원하는 포맷 형식으로 출력이 잘 되고 변환도 잘 되는 것으로 보인다.
이제 준비물 중 현재 서버의 날짜(today) - 35일 , 조회 시작 가능한 날짜 : maxStartDate를 만들어 보자.
우선 앞선 포스팅에도 적었듯이 Date 자료형의 덧셈 뺄셈을 하기 위해선 Calendar 객체를 사용해야 한다
현재 서버의 날짜(today) - 35일 계산 : Calendar
Calendar 클래스는 추상 클래스 임으로 선언을 할 때 getInstance()를 사용하여 생성자를 가져온다.
날짜 계산하는 테스트 코드를 만들어 보자.
간단하게 현재 날짜에 +1 일을 하는 로직을 먼저 테스트해 보았다.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class Test_DateCalCode {
public static void main(String[] args) throws ParseException {
Date today = new Date(); // 오늘 날짜
System.out.println(" 오늘 날짜 : " + today);
System.out.println(" 오늘 날짜 포멧 후 : " + dateToStr(today));
// Calendar 객체 선언
Calendar cal = Calendar.getInstance();
// Date 객체의 시간을 가져와 Calendar 객체에 대입
cal.setTime(today);
// Calendar 객체에 날짜를 1일 추가
cal.add(Calendar.DATE, 1);
// Calendar 객체의 날짜를 Date 객체로 변환
String strToday = dateToStr(cal.getTime());
System.out.println(" 오늘 날짜 계산 후 +1일 : " + strToday);
}
// Date 포멧 지정 후 String으로 리턴 함
private static String dateToStr(Date paramDate) throws ParseException {
SimpleDateFormat dashDateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dashDateFormat.format(paramDate);
}
// String 에서 Date로 리턴
private static Date strToDate(String strDate) throws ParseException {
SimpleDateFormat dashDateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dashDateFormat.parse(strDate);
}
}
잘 계산되는 것을 확인했고, -35일을 해보자!
방금 전 소스에서 수정할 것은 cal.add(Calendar.DATE, -35);로 수정하면 된다.
날짜를 더할 때는 양수로 작성하고 , 날짜를 뺄셈 할 때는 - 를 입력해 음수로 작성하면 된다.
// Calendar 객체 선언
Calendar cal = Calendar.getInstance();
// Date 객체의 시간을 가져와 Calendar 객체에 대입
cal.setTime(today);
// Calendar 객체에 날짜를 1일 추가
cal.add(Calendar.DATE, -35);
// Calendar 객체의 날짜를 Date 객체로 변환
String strToday = dateToStr(cal.getTime());
System.out.println(" 오늘 날짜 - 35일 계산 후 : " + strToday);
정확히 -35일이 되었고 월 또한 바뀐 것이 확인되었다.
나는 연이나 월을 조작하는 기능이 필요한 것이 아님으로 일을 기준으로 계산을 했고.
해당 기능을 분리해 메서드로 만들었다.
// 날짜 계산 리턴타입 String
private static String dateCalculating(String strDate, int day) throws Exception {
SimpleDateFormat dtFormat = new SimpleDateFormat("yyyy-MM-dd");
Calendar cal = Calendar.getInstance();
Date dt = dtFormat.parse(strDate);
cal.setTime(dt);
cal.add(Calendar.DATE, -day);
return dtFormat.format(cal.getTime());
}
날짜 계산 코드
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class Test_DateCalCode {
public static void main(String[] args) throws Exception {
// 입력받은 조회 시작일 String
String startDate = "2024-07-20";
// 입력받은 조회 종료일 String
String endDate = "2024-08-10";
// 서버상 오늘 날짜
Date today = new Date();
// 서버상 오늘 날짜 - 35일을 뺀 날짜
String maxStartDate = dateCalculating(dateToStr(today), 35);
System.out.println("클라이언트에게 입력받은 조회 시작일 : " + startDate);
System.out.println("클라이언트에게 입력받은 조회 종료일 : " + endDate);
System.out.println(" 현재 시간의 날짜 : " + dateToStr(today));
System.out.println(" 현재 시간의 날짜 -35일 결과 : " + maxStartDate);
}
// Date 포멧 지정 후 String으로 리턴 함
private static String dateToStr(Date paramDate) throws ParseException {
SimpleDateFormat dashDateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dashDateFormat.format(paramDate);
}
// String 에서 Date로 리턴
private static Date strToDate(String strDate) throws ParseException {
SimpleDateFormat dashDateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dashDateFormat.parse(strDate);
}
// 날짜 계산 리턴타입 String
private static String dateCalculating(String strDate, int day) throws Exception {
SimpleDateFormat dtFormat = new SimpleDateFormat("yyyy-MM-dd");
Calendar cal = Calendar.getInstance();
Date dt = dtFormat.parse(strDate);
cal.setTime(dt);
cal.add(Calendar.DATE, -day);
return dtFormat.format(cal.getTime());
}
}
검증을 위한 로직
방금 전 스크린샷을 보면 클라이언트에게 입력받은 조회 시작일과 종료일은 정상범주안에 있다.
이제 Date 객체의 메서드를 이용해 이 4가지의 날짜를 비교하는 로직을 만들어 보자.
내가 사용할 메서드는 총 3가지이다.
조금 이해하기 쉽도록 작성해 보았다. (내 기준에만 쉬운 건지..)
A equals(B) : A와 B의 날짜가 같은지? ( A == B? true : false)
A befor(B) : A와 B의 날짜 중 A날짜가 B날짜의 이전인지? (A < B ? true : false)
A after(B) : A와 B의 날짜 중 A날짜가 B날짜의 이후인지? (A > B ? true : false)
방금 전 만든 날짜들을 기준으로 타임라인 그림을 그려보자면 이러하다
|--------|maxStartDate|------|startDate|----------------------------|endDate|---|today|----------|
검증해야 할 목록을 정해놓았는데 그대로 코드를 작성해 본다
1. startDate가 maxStartDate보다 이전이면 조회 시작 날짜를 maxStartDate로 설정
2. startDate가 today보다 이후라면 조회 시작 날짜를 maxStartDate 설정
3. startDate가 maxStartDate 이후라면 startDate 값은 그대로 유지
4. endDate와 today가 동일하면 endDate를 today로 설정
5. endDate와 today를 비교하여 today보다 이전이라면 endData 값은 그대로 유지
6. endDate와 today를 비교하여 today보다 이후라면 endDate를 today로 설정
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class Test_DateCalCode {
public static void main(String[] args) throws Exception {
// 입력받은 조회 시작일 String
String startDate = "2024-07-20";
// 입력받은 조회 종료일 String
String endDate = "2024-08-10";
// 서버상 오늘 날짜
Date today = new Date();
// 서버상 오늘 날짜 - 35일을 뺀 날짜
String maxStartDate = dateCalculating(dateToStr(today), 35);
// String -> Date 객체로 모두 변환해 준다
// 클라이언트 조회 요청 시작일
Date startDt = strToDate(startDate);
// 클라이언트 조회 요청 종료일
Date endDt = strToDate(endDate);
// 조회 시작 가능 시작일(서버-35일)
Date maxStartDt = strToDate(maxStartDate);
System.out.println("[ 1. startDt maxStartDt보다 이전인가 ? 예상 false ] \t>>> " + startDt.before(maxStartDt));
System.out.println("[ 2. startDt가 today보다 이후인가 ? 예상 false ] \t>>> " + startDt.after(today));
System.out.println("[ 3. startDt가 maxStartDt보다 이후인가 ? 예상 true ] \t>>> " + startDt.after(maxStartDt));
System.out.println("[ 4. endDt가 today와 동일한가 ? 예상 false ] \t>>> " + endDt.equals(today));
System.out.println("[ 5. endDt가 today보다 이전인가 ? 예상 true ]\t>>> " + endDt.before(today));
System.out.println("[ 6. endDt가 today보다 이후인가 ? 예상 false ]\t>>> " + endDt.after(today));
}
// Date 포멧 지정 후 String으로 리턴 함
private static String dateToStr(Date paramDate) throws ParseException {
SimpleDateFormat dashDateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dashDateFormat.format(paramDate);
}
// String 에서 Date로 리턴
private static Date strToDate(String strDate) throws ParseException {
SimpleDateFormat dashDateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dashDateFormat.parse(strDate);
}
// 날짜 계산 리턴타입 String
private static String dateCalculating(String strDate, int day) throws Exception {
SimpleDateFormat dtFormat = new SimpleDateFormat("yyyy-MM-dd");
Calendar cal = Calendar.getInstance();
Date dt = dtFormat.parse(strDate);
cal.setTime(dt);
cal.add(Calendar.DATE, -day);
return dtFormat.format(cal.getTime());
}
}
예상 결과와 테스트코드 결과가 모두 같았다.
중간에 날짜를 바꿔서 해봤는데 비정상 일 때도 원했던 결과가 나왔다.
테스트를 마쳤으니 이걸 소스에 대입을 했는데 하고 보니 뭔가 소스코드가 어지러워 보였다.
고민을 하던 중 메서드 분리도 분리이지만 어차피 하나의 객체를 만들어서 해당 로직을 자동으로 하게 하는 건 어떨까 싶었다.
그래서 나는 하나의 Class를 만들어 지금까지 만들었던 기능들을 메서드로 추가했고.
get을 이용하여 가져다 사용할 수 있도록 구도를 만들었다.
리팩토링 후 테스트코드
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class DateInfo {
// 클라이언트에서 전달받은 시작일
private Date startDate;
// 클라이언트에서 전달받은 종료일
private Date endDate;
// 서버 오늘 날짜
private Date today;
// 서버 오늘 날짜 - 35일 , 최대 조회 가능한 시작일
private Date maxStartDate;
// 생성자
public DateInfo(String start, String end) throws Exception {
Date today = new Date();
String paramToday = dateToStr(today);
String maxStartDate = dateCalculating(paramToday, 35);
// 호출시 전역변수 생성 후 대입
this.startDate = strToDate(start); // 시작일
this.endDate = strToDate(end); // 종료일
this.today = strToDate(paramToday); // 서버날짜
this.maxStartDate = strToDate(maxStartDate); // 최대조회 가능한 시작일(서버날짜 - 35일)
// 날짜 비교 후 재 대입 처리
inputResultDate();
}
// 날짜 비교 후 대입처리
private void inputResultDate() throws ParseException {
// 조회 요청 종료날짜가 서버날짜랑 같다면 서버날짜를 대입 , 다르다면 조회 요청 종료날짜를 대입
endDate = endDate.equals(today) ? today : endDate;
// 조회 요청 종료날짜가 서버날짜보다 이전이라면 조회 요청 종료날짜를 대입, 아니라면 서버날짜로 대입
endDate = endDate.before(today) ? endDate : today;
// 조회 요청 종료날짜가 서버날짜-35일 이후라면 조회 요청 종료날짜를 대입, 아니라면 서버날짜를 대입
endDate = endDate.after(maxStartDate) ? endDate : today;
// 조회 요청 시작날짜가 서버날짜-35일 이후라면 조회 요청 시작날짜를 대입 , 아니라면 서버날짜-35일을 대입
startDate = startDate.after(maxStartDate) ? startDate : maxStartDate;
// 조회 요청 시작날짜가 서버날짜 이전이라면 요청 시작 날짜를 대입 , 아니라면 서버 날짜를 대입
startDate = startDate.before(today) ? startDate : today;
}
// Date 포멧 지정 후 String으로 리턴 함
private static String dateToStr(Date paramDate) throws ParseException {
SimpleDateFormat dashDateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dashDateFormat.format(paramDate);
}
// String 에서 Date로 리턴
private static Date strToDate(String strDate) throws ParseException {
SimpleDateFormat dashDateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dashDateFormat.parse(strDate);
}
// 날짜 계산 리턴타입 String
private static String dateCalculating(String strDate, int day) throws Exception {
SimpleDateFormat dtFormat = new SimpleDateFormat("yyyy-MM-dd");
Calendar cal = Calendar.getInstance();
Date dt = dtFormat.parse(strDate);
cal.setTime(dt);
cal.add(Calendar.DATE, -day);
return dtFormat.format(cal.getTime());
}
public String getStartDate() throws ParseException {
return dateToStr(startDate);
}
public String getEndDate() throws ParseException {
return dateToStr(endDate);
}
}
public class Test_DateCalCode {
public static void main(String[] args) throws Exception {
// 클라이언트에게 요청받은 시작 날짜 : 2024-05-10, 종료 날짜 : 2024-09-10
// 클라이언트에게 요청 받은 시작과 종료 날짜는 비정상범주에 있는 날짜이다.
// 그래서 DateInfo 객체 안에 있는 startDate와 endDate는 서버일-35일 부터 서버일까지로 변경되어 대입되야 한다.
String startDate = "2024-05-10";
String endDate = "2024-09-10";
DateInfo date = new DateInfo(startDate, endDate);
System.out.println(" 클라이언트 요청 시작일 : " + startDate);
System.out.println(" > 검증 처리 후 변환된 시작일 : " + date.getStartDate());
System.out.println(" 클라이언트 요청 종료일 : " + endDate);
System.out.println(" > 검증 처리 후 변환된 종료일 : " + date.getEndDate());
System.out.println("====================================================================");
startDate = "2024-08-01";
endDate = "2024-09-01";
date = new DateInfo(startDate, endDate);
System.out.println(" 클라이언트 요청 시작일 : " + startDate);
System.out.println(" > 검증 처리 후 변환된 시작일 : " + date.getStartDate());
System.out.println(" 클라이언트 요청 종료일 : " + endDate);
System.out.println(" > 검증 처리 후 변환된 종료일 : " + date.getEndDate());
System.out.println("====================================================================");
startDate = "2024-02-01";
endDate = "2024-08-01";
date = new DateInfo(startDate, endDate);
System.out.println(" 클라이언트 요청 시작일 : " + startDate);
System.out.println(" > 검증 처리 후 변환된 시작일 : " + date.getStartDate());
System.out.println(" 클라이언트 요청 종료일 : " + endDate);
System.out.println(" > 검증 처리 후 변환된 종료일 : " + date.getEndDate());
System.out.println("====================================================================");
startDate = "2024-08-01";
endDate = "2024-08-10";
date = new DateInfo(startDate, endDate);
System.out.println(" 클라이언트 요청 시작일 : " + startDate);
System.out.println(" > 검증 처리 후 변환된 시작일 : " + date.getStartDate());
System.out.println(" 클라이언트 요청 종료일 : " + endDate);
System.out.println(" > 검증 처리 후 변환된 종료일 : " + date.getEndDate());
System.out.println("====================================================================");
}
}
테스트를 하면서 비정상 값과 정상값을 비교했고 정상적인 출력을 확인했다.
그리고 Spring 서비스단 코드에 대입을 하려고 하는데 추가적인 작업이 또 필요했다 , 그 부분은 얼마 안 되는 분량이지만
날짜 데이터와는 다른 내용임으로 다음 포스팅 때 작성하도록 하겠다.
포스팅을 마무리하며 .
현업을 하면서 날짜를 핸들링하는 로직을 구현하는 일이 종종 있다고 들었다.
아직 새내기라 그런지 개발 요구사항을 전달 받게 되면 검색하고 테스트하고 적용하고 하는데에 오랜 시간이 드는 것 같다.
지금은 배우는 시기이기 때문에 늦더라도 포기하지 않고 계속 공부하고 도전하려고 한다.
이렇게 공부했던 내용들을 정리하며 개발을 뚝딱뚝딱 잘하는 개발자가 되길 바라며 포스팅을 마무리하겠다!👩🏻💻
'👩🏻💻 𝐋𝐚𝐧𝐠𝐮𝐚𝐠𝐞 > ⠀⠀⠀⠀ Jᴀᴠᴀ' 카테고리의 다른 글
Java7의 날짜계산 : Date, Calendar, SimpleDateFormat(1) (0) | 2024.08.16 |
---|---|
Java의 상수, 매직넘버란 ? (0) | 2024.04.28 |
일급 컬렉션 (First Class Collection) (0) | 2024.04.28 |
2023/11/30🤘 디자인패턴 - State Pattern & SingleTon Pattern (1) | 2023.12.05 |
2023/11/27 ~ 2023/11/29🤘 JAVA GUI Swing (0) | 2023.12.04 |