λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
πŸ“• Spring Framework/Spring Project

[회고] ν”„λ‘œμ νŠΈ (νŠΈλ ˆμ΄λ„ˆ 쀑계 ν”Œλž«νΌ)

by GroovyArea 2022. 4. 18.

β–Ά ν”„λ‘œμ νŠΈ 끝! (2022.3.23 ~ 2022.4.13)

λ“œλ””μ–΄ νŒŒμ΄λ„ ν”„λ‘œμ νŠΈκ°€ 끝이 났닀! 회의 μ‹œμž‘μΌμ΄ 벌써 μ—Šκ·Έμ œ 같은데.. 눈 λ‚΄λ¦¬λŠ” 첫 νšŒμ˜μΌμ— μ‹œμž‘μ„ ν•΄μ„œ λλ‚˜κ³  λ³΄λ‹ˆ λ²šκ½ƒμ΄ λ–¨μ–΄μ§ˆ λ•Œμ΄λ‹€.
쀑간에 μž₯μ—Όμ΄λ‹ˆ, μ½”λ‘œλ‚˜λ‹ˆ ν•΄μ„œ μ•„ν”„μ§€λ§Œ μ•Šμ•˜μ–΄λ„ 더 빨리 λλ‚˜κ³  λ²šκ½ƒ 보러 갔을 텐데 μ°Έ 아쉽닀. 또 μ½”λ‘œλ‚˜λΌ μžκ°€ 치료λ₯Ό 일주일 λ™μ•ˆ ν•΄μ„œ λ„ˆλ¬΄ λ‹΅λ‹΅ν•΄μ„œ 더 속도가 느렀쑌던 감도 μžˆλ‹€. μ—­μ‹œ λ‚˜λŠ” 카페λ₯Ό λ‚˜κ°€μ•Ό λ˜λ‚˜ 보닀! 
 

β–Ά ν”„λ‘œμ νŠΈ μ „ 회의 이슈 사항듀

ν”„λ‘œμ νŠΈλ₯Ό μ›ν•˜λŠ” μ‚¬λžŒλ“€λΌλ¦¬ 처음 μ‘°λ₯Ό 짜고 μ§„ν–‰ν•˜λ©΄μ„œ ν™•μ‹€νžˆ 이전 ν”„λ‘œμ νŠΈ λ•Œ 보닀 μ›ν• ν•˜κ²Œ μ§„ν–‰λ˜μ—ˆλ˜ μ€€λΉ„ κ³Όμ •μ΄μ—ˆλ˜ 것 κ°™λ‹€. ν•œ 가지 μ•„μ‰¬μš΄ 점은 UI κ΄€λ ¨ νšŒμ˜κ°€ μ’…λ£Œλ˜κ³  DB 섀계 뢀뢄에 λ“€μ–΄κ°„ ν›„λ‘œλΆ€ν„° 거의 νŒ€μž₯ν˜•κ³Ό λ‚˜λ§Œ μ–˜κΈ°ν–ˆλ˜ 점이닀 γ…œγ…œ κ·Έλž˜λ„ μ–΄λŠ 정도 핡심적인 뢀뢄은 각자 생각을 λ‚΄μ£Όμ…”μ„œ μ•„μ‰¬μš΄ 점이 ν•΄μ†Œλ˜κΈ΄ ν–ˆμ—ˆλ‹€! 
 
SQL을 미리 μ§œλ†“μ§€ μ•Šμ€ 뢀뢄은 λ‚˜μ˜μ§€ μ•Šμ•˜λ˜ 것 κ°™λ‹€. ν”„λ‘œμ νŠΈλ₯Ό ν•˜λ©° μˆ˜μ •μ΄ 많이 ν•„μš”ν•œ λΆ€λΆ„μ΄μ—ˆκΈ°μ— μ΄λ²ˆμ—λŠ” 과감히 νŒ¨μŠ€ν–ˆλ‹€. κ·Έ μ‹œκ°„μ— 학원 볡슡과 λ”°λ‘œ 곡뢀λ₯Ό μ‹œμž‘ν•΄μ„œ μ–»μ–΄κ°„ 뢀뢄이 λ§Žμ•„ μ’‹μ•˜λ‹€!
 
View 뢀뢄을 미리 λ§Œλ“€μ–΄ 두지 μ•Šμ•˜λ˜ 것은 μ’€ 아쉬웠닀! 미리 짜 λ†“μžκ³  ν•©μ˜λ₯Ό λ΄€μ—ˆμ§€λ§Œ 각자의 사정이 μžˆμ—ˆκΈ°μ— μ•„μ‰½κ²Œ λ˜μ—ˆμ§€λ§Œ Frontλ₯Ό λ§‘μœΌμ‹  νŒ€μž₯ ν˜•μ΄ 속도λ₯Ό 잘 λ‚΄μ–΄μ£Όμ…”μ„œ ν”„λ‘œμ νŠΈλ₯Ό μ§„ν–‰ν•˜λ©΄μ„œ 생각보닀 빨리 λ§Œλ“€μ–΄μ§„ λŠλ‚Œμ΄ μžˆμ—ˆλ‹€.
 
λ§ˆμ§€λ§‰μœΌλ‘œ 이번 ν”„λ‘œμ νŠΈμ—μ„œ ν˜‘μ—… 관리도ꡬλ₯Ό Svn은 μ‚¬μš©μ„ ν•΄λ³΄μ•˜μœΌλ‹ˆ Git으둜 ν•΄λ³΄μžλΌκ³  μ œμ•ˆμ„ ν•΄μ„œ μ €μž₯μ†Œλ₯Ό μƒμ„±ν•˜κ³  각각의 브랜치λ₯Ό λ§Œλ“€κ³  gitignore νŒŒμΌμ„ λ§Œλ“œλŠ” 것에 μ’€ μ• λ₯Ό λ¨Ήμ—ˆμ§€λ§Œ, νŒ€μ›λ“€μ˜ λ…Έλ ₯으둜 κ²°κ΅­ 잘 ν•΄κ²°λ˜μ—ˆλ˜ κ²½ν—˜μ΄ μžˆλ‹€!
 
ν”„λ‘œμ νŠΈλ₯Ό μ‹œμž‘ν•˜λ©° μš°λ¦¬λŠ” Front 와 Back으둜 2:2 κ΅¬λ„λ‘œ μ§„ν–‰ν–ˆλ‹€. Backμ΄μ—ˆλ˜ λ‚˜λŠ” λ‹Ήμ—°νžˆ Sql을 μž‘μ„±ν•˜λŠ” 뢀뢄이 λ¨Όμ €μ˜€κΈ°μ—, νŒ€μ› λˆ„λ‚˜μ™€ 같이 sql μ •μ˜λ₯Ό ν•΄λ‚˜κ°”λ‹€. 일전에 λˆ„λ‚˜κ°€ μžμ‹ μ΄ μ—†λ‹€κ³  λ§ν–ˆμ—ˆλ˜ 것과 λ‹€λ₯΄κ²Œ μ˜μ™Έλ‘œ λ‚΄κ°€ λ§‰νžˆλ˜ λΆ€λΆ„μ—μ„œ λͺ…μΎŒν•œ ν•΄κ²° 쿼리문을 λ˜μ Έμ€˜μ„œ λ§Žμ€ 도움이 λ˜μ—ˆλ‹€. 특히, Joinκ³Ό κ΄€λ ¨λœ 뢀뢄은 μ‹€μ œλ‘œ μ‚¬μš©ν•΄ λ³Έ 적이 μ—†μ—ˆλŠ”λ° λˆ„λ‚˜κ°€ 귀띔을 쏙쏙 ν•΄μ€˜μ„œ μ˜ˆμ •λ³΄λ‹€ 빨리 λλ‚΄μ„œ λ„ˆλ¬΄ κ°μ‚¬ν–ˆλ‹€.

β–Ά νŒŒμ΄λ„ ν”„λ‘œμ νŠΈ 진행 쀑 μ§λ©΄ν–ˆλ˜ λ¬Έμ œλ“€κ³Ό 해결법

SQL μž‘μ„±μ΄ λλ‚œ ν›„ 본격적으둜 Controller μž‘μ—…μ— λ“€μ–΄κ°€λ©΄μ„œ 큰 λ¬Έμ œκ°€ 총 μ„Έ 가지 정도 μžˆμ—ˆλ‹€.
첫 째둜,  DB에 μžˆλŠ” μƒμˆ˜μ™€ λ§€ν•‘ν•˜λŠ” 뢀뢄이닀. DB에 μ €μž₯ν•˜λŠ” λ°μ΄ν„°λŠ” μˆ«μžμ΄μ§€λ§Œ μ‹€μ œλ‘œ ν‘œμ‹œν•΄μ•Ό ν•  뢀뢄은 λ¬Έμžμ—΄μ΄μ—ˆκΈ°μ— 이런 뢀뢄을 λ‹¨μˆœνžˆ Viewμ—μ„œ JSTL <c:if> λ₯Ό μ‚¬μš©ν•΄μ„œ ν•  수 μžˆμ—ˆμ§€λ§Œ λ”λŸ¬μš΄ μ½”λ“œκ°€ 될 것이 λΆ„λͺ…ν•˜λ―€λ‘œ 전에 κ³΅λΆ€ν–ˆλ˜ enum νŒ¨ν‚€μ§€λ₯Ό λ”°λ‘œ μƒμ„±ν•˜μ—¬ μ—¬λŸ¬ μƒμˆ˜λ₯Ό μ •μ˜ν•œ 클래슀λ₯Ό λ§Œλ“€μ–΄ 맀핑을 ν•˜λ―€λ‘œ 해결을 ν–ˆλ‹€. λ°°μ› λ˜ 것을 μ‹€μ œλ‘œ μ μš©ν•˜λ‹ˆ 더 와 λ‹Ώμ•˜λ‹€. νŒ€μ› λˆ„λ‚˜λ„ λ”°λ‘œ λ°°μš°μ§€ μ•Šμ•˜μ§€λ§Œ κΆκΈˆν–ˆλ˜ 뢀뢄에 λŒ€ν•΄ μ•Œκ²Œ λ˜μ–΄ μ’‹μ•˜λ‹€κ³  ν•˜λ‹ˆ νŒ€μ›μ—κ²Œ 지식을 잘 μ „λ‹¬ν•œ 것 κ°™μ•„ 더 λΏŒλ“―ν–ˆλ‹€.
 

μƒμ„±ν•œ enum νŒ¨ν‚€μ§€

public enum PtOnceExperienceEnum {

	ν•œλ‹¬λ―Έλ§Œ(1), μ‚Όκ°œμ›”(2), μœ‘κ°œμ›”(3), 일년이상(4);
	
	private final int ptOnceExperience;
	
	private PtOnceExperienceEnum(final int ptOnceExperience) {
		this.ptOnceExperience = ptOnceExperience;
	}
	
	public static PtOnceExperienceEnum of(final int number) {

		switch (number) {

		case 2:
			return μ‚Όκ°œμ›”;
		case 3:
			return μœ‘κ°œμ›”;
		case 4:
			return 일년이상;
		default:
			return ν•œλ‹¬λ―Έλ§Œ;
		}

	}

	public int getValue() {
		return ptOnceExperience;
	}
	
}

=> 클래슀 ν•œ μ˜ˆμ‹œ
 
λ‘˜ 째둜,  파일과의 싸움이닀..! 파일 μ—…λ‘œλ“œ, λ‹€μš΄λ‘œλ“œμ— λŒ€ν•΄ μ „ν˜€ μƒκ°ν•˜μ§€ λͺ»ν–ˆμ—ˆλŠ”데.. 진행쀑에 νŠΈλ ˆμ΄λ„ˆ μ‹ μ²­κ³Ό ν¬μŠ€νŒ… μž‘μ„± 뢀뢄을 μ‹œμž‘ν•œ μˆœκ°„ 정신이 잠깐 λ‚˜κ°”μ—ˆλ‹€κ³  μƒκ°ν–ˆλ‹€.. DB 섀계 μ‹œμ— λΆ„λͺ…νžˆ 이미지 파일 μ»¬λŸΌμ„ λ§Œλ“€μ—ˆλŠ”λ°.. γ…‹γ…‹γ…‹γ…‹γ…‹
 μ˜ˆμ œλ‘œ 배운 νŒŒμΌμ—…λ‘œλ“œλŠ” 단일 νŒŒμΌμ΄λ‹€. ν•˜μ§€λ§Œ 우린 λ‹€μ€‘νŒŒμΌ 심지어 개수 μ‘°μž‘μ΄ κ°€λŠ₯ν•œ λ‹€μ€‘νŒŒμΌμ΄λ‹€..그래.. Insert λΆ€ν„° μ‹œμž‘ν•΄λ³΄μž.. ν•˜λ©° νŠΈλ ˆμ΄λ„ˆ 신청을 μ‹œμž‘ν–ˆλ‹€. νŠΈλ ˆμ΄λ„ˆ μ‹ μ²­ μ‹œ ν”„λ‘œν•„ μ΄λ―Έμ§€λŠ” 1κ°œμ΄λ‹ˆ 이 μΉœκ΅¬λŠ” 쉽닀. ν•˜μ§€λ§Œ λ¬Έμ œλŠ” μˆ˜μƒκ²½λ ₯ 이미지이닀. κ²Œλ‹€κ°€ κ·Έ 이미지에 ν•΄λ‹Ήν•˜λŠ” 문자 데이터가 μžˆλ‹€. μš” λ†ˆμ΄ μ’€ 지μ˜₯μ΄μ—ˆμ§€λ§Œ μ΅œλŒ€ κ°œμˆ˜κ°€ 4개둜 μ •ν•΄μ Έ μžˆμ—ˆκΈ°μ— Iterator둜 filenamesλ₯Ό λ°›μ•„ for문을 돌렀 ν•΄κ²°ν–ˆλ‹€. InsertλŠ” κ·Έλž˜λ„ μ‰¬μš΄νŽΈμ΄μ—ˆλ‹€. λ¬Έμ œλŠ” μˆ˜μ •μ΄λ‹€.. μ–˜κ°€ 제일 λ¬Έμ œλ‹€ μ§„μ§œ 3일 κ±Έλ Έλ‹€.. 

ν”„λ‘œν•„ 이미지 μΆ”κ°€ν•˜λŠ” μ œμ–΄λ¬Έ μ΄ν•˜ 둜직 μƒλž΅
μˆ˜μƒκ²½λ ₯ 닀쀑 파일 μ €μž₯ Iterator λŒ€μ‹  getFiles둜 λ°›μŒ

μˆ˜μ •μ΄λΌ.. νŠΈλ ˆμ΄λ„ˆ 정보 μˆ˜μ •λΆ€ν„° κ°€μž. νŠΈλ ˆμ΄λ„ˆ μˆ˜μ •μ€ ν”„λ‘œν•„ 이미지 1개 μˆ˜μ • 및 μ΅œλŒ€ 4개인 μˆ˜μƒκ²½λ ₯ 이미지듀 μˆ˜μ •μ΄λ‹€. μ΅œλŒ€ λ¬Έμ œλŠ” μˆ˜μ • μ‹œ input νƒœκ·Έμ—μ„œ file 속성은 μ‚¬μš©μžμ˜ μž…λ ₯ 값이 없을 λ•Œ ""이 λ„£μ–΄μ§„λ‹€λŠ” 점이닀. μ΅œμ•…μ΄λ‹€. μˆ˜μ •μ„ μ•ˆ ν•  κ²½μš°μ—” κ·Έλƒ₯ 둬야 ν•˜λŠ”λ°. κ·Έλƒ₯ 두면 ""이 λ„˜μ–΄μ˜¨λ‹€λ‹ˆ 정말 μ΅œμ•…μ΄λ‹€. ν•˜μ§€λ§Œ κΈ°μ‘΄ 데이터λ₯Ό value에 λ„£κ³  input hidden으둜 값을 λ„˜κ²¨ 파일이 변경이 될 경우, κ·ΈλŒ€λ‘œ λ‘˜ 경우λ₯Ό javascript둜 μ‘°μž‘ν•˜μ—¬ hidden valueλ₯Ό μ‘°μž‘ν•˜μ—¬ ν•΄κ²°ν–ˆλ‹€. 

μˆ˜μƒκ²½λ ₯ 데이터 hidden
ν”„λ‘œν•„ 이미지 λ³€κ²½ 둜직

// μˆ˜μƒ κ²½λ ₯ 이미지가 μ €μž₯될 μ„œλ²„ 디렉토리
		String uploadAwardImagesDirectory = context.getServletContext().getRealPath("/resources/assets/awardImages");

		// κΈ°μ‘΄ DB 데이터듀
		String[] hiddenAwardImages = request.getParameterValues("hiddenAwardImages"); // hidden μˆ˜μƒ κ²½λ ₯ 이미지 이름듀
		String[] hiddenAwardNumbers = request.getParameterValues("hiddenAwardNumbers"); // hidden award PKλ“€

		String[] currentAwardNumbers = request.getParameterValues("currentAwardNumbers"); // DB μˆ˜μƒ κ²½λ ₯ PKλ“€

		// μˆ˜μƒ κ²½λ ₯ 사진 이미지듀
		List<MultipartFile> modifyAwardImages = request.getFiles("aImage");

		// μˆ˜μƒ κ²½λ ₯ 사진 μ„€λͺ…λ“€
		String[] modifyAwardContents = request.getParameterValues("aContent");

		// κΈ°μ‘΄ hidden갯수 = DB λ ˆμ½”λ“œ 수
		// => hidden의 κ°―μˆ˜κ°€ λ³€ν•˜λŠ” 경우 : κΈ°μ‘΄ νŒŒμΌμ„ μ‚­μ œν•˜λŠ” 경우 => (-) λ²„νŠΌμ„ λˆŒλ €μ„ λ•Œ
		// κ°œμˆ˜κ°€ 같을 땐 변함 μ—†λ‹€ => 변함 μ—†μŒ

		// λ°›μ•„μ˜¨ 파일이 μ—†λ‹€ : μœ μ§€ or μ‚­μ œ

		List<String> awardImgList = new ArrayList<String>();

		for (int a = 0; a < currentAwardNumbers.length; a++) {
			awardImgList.add(awardService.getAward(Integer.parseInt(currentAwardNumbers[a])).getAwardImage());
		}

		// hidden 갯수 λž‘ DBκ°―μˆ˜κ°€ μ•ˆλ§žμ„λ•Œ(κ·Έλƒ₯ μ‚­μ œν–ˆμ„λ•Œ)
		// hidden 갯수 < DB λ ˆμ½”λ“œ 갯수
		if (hiddenAwardNumbers.length < currentAwardNumbers.length) {

			// 1. κΈ°μ‘΄ 파일 μ‚­μ œ -> ν•΄λ‹Ή DB μ‚­μ œ
			for (int i = 0; i < awardImgList.size(); i++) {

				// hiddenμ—λŠ” μ—†μ§€λ§Œ DB λ°°μ—΄μ—” μžˆλŠ” 경우 db λ ˆμ½”λ“œλ₯Ό μ‚­μ œ -> ν•΄λ‹Ή μΈλ±μŠ€λŠ” 곧 awardNo PK
				if (Arrays.asList(hiddenAwardImages).contains(awardImgList.get(i)) == false) {

					new File(uploadAwardImagesDirectory,
							awardService.getAward(Integer.parseInt(currentAwardNumbers[i])).getAwardImage()).delete(); // 파일
																														// μ‚­μ œ

					awardService.removeAward(Integer.parseInt(currentAwardNumbers[i])); // ν•΄λ‹Ή DB λ ˆμ½”λ“œ μ‚­μ œ
				}
			}
		} else {// νžˆλ“ μ˜ ν¬κΈ°λŠ” 같은데 (λ³€κ²½λœ 파일 μ‚­μ œ)

			for (int hid = 0; hid < currentAwardNumbers.length; hid++) {
				if (hiddenAwardImages[hid].equals(awardImgList.get(hid)) == false) { // νžˆλ“ μ˜ 값이 DBκ°’μ΄λž‘ μΌμΉ˜ν•˜μ§€ μ•ŠμœΌλ©΄
					// κΈ°μ‘΄ DBκ°’κ³Ό κΈ°μ‘΄ νŒŒμΌμ„œλ²„μ—μ„œ μ‚­μ œ
					new File(uploadAwardImagesDirectory,
							awardService.getAward(Integer.parseInt(currentAwardNumbers[hid])).getAwardImage()).delete(); // νŒŒμΌμ‚­μ œ

					awardService.removeAward(Integer.parseInt(currentAwardNumbers[hid])); // ν•΄λ‹Ή DB λ ˆμ½”λ“œ μ‚­μ œ

				}
			}
		}

		// DBμ΅œμ‹ ν™”
		List<Award> awardList3 = awardService.getAwardList(trainer.getTrainerNo());
		String[] imgArray = new String[awardList3.size()];
		int i = 0;
		for (Award award : awardList3) {
			imgArray[i] = award.getAwardImage();
			i++;
		}

		for (int c = 0; c < modifyAwardImages.size(); c++) {

			// λ°›μ•„μ˜¨ 파일이 μžˆλ‹€ : μΆ”κ°€
			if (!modifyAwardImages.get(c).isEmpty()) {

				// DB값에 받은 파일 λͺ…이 μžˆλŠ”μ§€ 확인.
				if (!Arrays.asList(imgArray).contains(modifyAwardImages.get(c).getOriginalFilename())) {// μ—†μœΌλ©΄

					// 이제 μΆ”κ°€λ§Œ ν•˜λ©΄λ¨.
					Award award = new Award();

					award.setTrainerNo(dbTrainer.getTrainerNo());
					award.setAwardImage(modifyAwardImages.get(c).getOriginalFilename());
					award.setAwardContent(modifyAwardContents[c]);

					File file = new File(uploadAwardImagesDirectory, modifyAwardImages.get(c).getOriginalFilename());

					String uploadFileName = modifyAwardImages.get(c).getOriginalFilename();

					// μ„œλ²„ 디렉토리에 μ „λ‹¬νŒŒμΌκ³Ό 같은 μ΄λ¦„μ˜ 파일이 μ‘΄μž¬ν•  경우 μ„œλ²„ 디렉토리에 μ €μž₯될 파일λͺ… λ³€κ²½
					int j = 0;
					while (file.exists()) {// μ„œλ²„ 디렉토리에 같은 μ΄λ¦„μ˜ 파일이 μžˆλŠ” 경우 반볡 처리
						j++;
						int index = modifyAwardImages.get(c).getOriginalFilename().lastIndexOf(".");

						uploadFileName = modifyAwardImages.get(c).getOriginalFilename().substring(0, index) + "_" + j
								+ modifyAwardImages.get(c).getOriginalFilename().substring(index);
						file = new File(uploadAwardImagesDirectory, uploadFileName);
					}

					modifyAwardImages.get(c).transferTo(file);

					awardService.addAward(award);
				}
			}
		}

=> 쑰금 κΈΈμ§€λ§Œ κ·Έλž˜λ„ ν•΄κ²° μ‚­μ œν•œ 경우 & κ·Έλƒ₯ λ‘μ—ˆμ„ 경우, μ„œλ²„ 파일 μ‚­μ œ μΆ”κ°€ μ—¬λΆ€ νŒλ‹¨ 및 DB μ΅œμ‹ ν™”
 
ν¬μŠ€νŒ… μž‘μ„± μΆ”κ°€λŠ” κ·Έλž˜λ„ λΉ„μŠ·ν•˜κΈ° λ•Œλ¬Έμ— 쉽닀. ν•˜μ§€λ§Œ λ‹€λ₯Έ ν…Œμ΄λΈ” λ‘κ°œμ˜ μΆ”κ°€κ°€ λ”°λ‘œ κ΄€λ¦¬ν•˜λŠ” μœ λ™μ μΈ 데이터 ν…Œμ΄λΈ”μ΄μ—ˆκΈ°μ— 더 νž˜λ“€λ‹€.. if문으둜 경우λ₯Ό 잘 λ‚˜λˆ„μ–΄ ν•΄κ²°ν–ˆλ‹€.

// ===============================PT 가격 μΆ”κ°€===============================

		String[] roundList = request.getParameterValues("round");
		String[] priceList = request.getParameterValues("roundPrice");

		int priceCount = 0;
		for (String round : roundList) {

			PtPricing ptPricing = new PtPricing();
			ptPricing.setPtPricingRound(Integer.parseInt(round));
			ptPricing.setPtPricingPrice(Integer.parseInt(priceList[priceCount]));
			ptPricing.setTrainerNo(trainerService.getTrainer(memberNo).getTrainerNo());

			ptPricingService.addPtPricing(ptPricing);

			priceCount++;
		}

		// ===============================PT μŠ€μΌ€μ₯΄ μΆ”κ°€===============================

		// 체크 λ°•μŠ€ μ„ νƒλœ μ• λ“€λ§Œ λ°›μ•„μ˜΄
		String[] workdays = request.getParameterValues("workdayCheck");
		String[] hour1s = request.getParameterValues("hour1");
		String[] minute1s = request.getParameterValues("minute1");
		String[] hour2s = request.getParameterValues("hour2");
		String[] minute2s = request.getParameterValues("minute2");

		String dayOff = request.getParameter("dayoff");
		String dayOffText = request.getParameter("dayOffText");

		int dayCount = 0;
		for (String workday : workdays) {

			Schedule schedule = new Schedule();
			String time = hour1s[dayCount] + ":" + minute1s[dayCount] + "~" + hour2s[dayCount] + ":"
					+ minute2s[dayCount];
			schedule.setScheduleHours(time);
			schedule.setScheduleWorkday(ScheduleWorkdayEnum.of(Integer.parseInt(workday)).getValue());
			schedule.setTrainerNo(trainerService.getTrainer(memberNo).getTrainerNo());

			scheduleService.addSchedule(schedule);

			dayCount++;
		}

		// 휴무일 정보도 μžˆμ„ μ‹œ
		if ((dayOff != null) && (!dayOffText.equals(""))) {
			Schedule schedule = new Schedule();
			schedule.setScheduleWorkday(ScheduleWorkdayEnum.of(Integer.parseInt(dayOff)).getValue());
			schedule.setScheduleDayoff(dayOffText);
			schedule.setTrainerNo(trainerService.getTrainer(memberNo).getTrainerNo());

			scheduleService.addSchedule(schedule);
		}

=> 쑰금 κΈΈμ§€λ§Œ ν•΄κ²°
 

μ΄λ ‡κ²Œ ν”ŒλŸ¬μŠ€, λ§ˆμ΄λ„ˆμŠ€ λ²„νŠΌμœΌλ‘œ λ³€ν•˜λŠ” 값이기에 κ³¨μΉ«λ©μ–΄λ¦¬μž„

이제 ν¬μŠ€νŒ… μˆ˜μ •μ΄ λ¬Έμ œλ‹€. 이미지 및 2개의 μœ λ™μ μΈ 변경이 μžˆλŠ” ν…Œμ΄λΈ”μ΄μ—ˆκΈ°μ— 더 λ¬Έμ œλ‹€. νŒŒμΌμ„ μ‚­μ œ / μΆ”κ°€ / λ³€κ²½ν•  경우 μ œμ–΄λ¬ΈμœΌλ‘œ λ‚˜λˆ„μ–΄ 잘 μ²˜λ¦¬ν–ˆλ‹€. (μ½”λ“œλŠ” λ„ˆλ¬΄ κΈΈμ–΄μ„œ μƒλž΅ν•˜κ² λ‹€.. 이게 ν•΅μ‹¬μ΄κΈ΄ν•œλ°..) PT 가격 및 μŠ€μΌ€μ₯΄ μˆ˜μ •μ€ λ‹€ν–‰νžˆ 파일이 μ•„λ‹ˆμ—ˆκΈ°μ— 전체 μ‚­μ œ ν›„ μΆ”κ°€ν•˜λŠ” λ‘œμ§μ„ 생각해 겨우 ν•΄κ²°ν–ˆλ‹€.
 
λ§ˆμ§€λ§‰μœΌλ‘œ κΆŒν•œ 처리λ₯Ό ν•˜λŠ” 인터셉터 λΆ€λΆ„μ΄μ—ˆλŠ”λ°, 배운 κ±Έ μ–΄λ–»κ²Œ ν•΄κ²°ν• κΉŒ κ³ λ―Όν•˜λ‹€κ°€ νŒ€μž₯ν˜•μ΄ ꡬ글 검색을 톡해 μ—λ„ˆν…Œμ΄μ…˜μ„ ν™œμš©ν•˜κΈ°λ‘œ κ²°μ •ν–ˆλ‹€. ν™•μ‹€νžˆ XML에 직접 λ©”μ„œλ“œλ₯Ό λ“±λ‘ν•˜λŠ” 건 μ˜›λ‚  방식인 것 κ°™μ•˜κΈ°λ„ ν•œ μ΄μœ κ°€ μžˆμ—ˆλ‹€.

@Retention(RUNTIME)
@Target(PARAMETER)
public @interface AuthUser {

}

=> μ—λ„ˆν…Œμ΄μ…˜ μ •μ˜

@Retention(RUNTIME)
@Target({ TYPE, METHOD })
public @interface Auth {
	public enum Role {
		ALL, ADMIN, TRAINER, USER, USER_PRETRAINER, PRETRAINER_TRAINER, USER_PRETRAINER_ADMIN
	}

	public Role role() default Role.ALL;
}

=> κΆŒν•œ κ΄€λ ¨ enum μ„ μ–Έ 기본값은 전체 μ‚¬μš©μž!

public class AuthUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver{

	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
		// TODO Auto-generated method stub
		// 1. νŒŒλΌλ―Έν„°μ— @AuthUserκ°€ λΆ™μ–΄ μžˆλŠ”μ§€ , νƒ€μž…μ΄ Member인지 확인 
				if( supportsParameter(parameter) == false ) {
					// λ‚΄κ°€ 해석할 수 μžˆλŠ” νŒŒλΌλ―Έν„°κ°€ μ•„λ‹ˆλ‹€.
					return WebArgumentResolver.UNRESOLVED;
				}
				
				// 5. μ—¬κΈ°κΉŒμ§€ 진행이 λ˜μ—ˆλ‹€λ©΄, @AuthUserκ°€ λΆ™μ–΄μžˆκ³  νƒ€μž…μ΄ UserVO인 κ²½μš°μ΄λ‹€.
				HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
				HttpSession session = request.getSession();
				if( session == null) {
					return null;
				}
				
				return session.getAttribute("loginUserinfo");
	}
	
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		// 2. @AuthUserκ°€ λΆ™μ–΄ μžˆλŠ”μ§€ 확인
				AuthUser authUser = parameter.getParameterAnnotation(AuthUser.class);
				
				// 3. @AuthUserκ°€ μ•ˆλΆ™μ–΄ μžˆλŠ” 경우
				if( authUser == null ) {
					return false;
				}
				
				// 4. Member νƒ€μž…μ΄ μ•„λ‹Œ 경우
				if( parameter.getParameterType().equals(Member.class) == false) {
					return false;
				}
				
				return true;
	}
	
}

=> μ—λ„ˆν…Œμ΄μ…˜ κ΄€λ ¨ ν•Έλ“€λŸ¬ 리쑸버 κ΅¬ν˜„
 

@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws IOException {
		// 1. handler μ’…λ₯˜ 확인
		// μš°λ¦¬κ°€ 관심 μžˆλŠ” 것은 Controller에 μžˆλŠ” λ©”μ„œλ“œμ΄λ―€λ‘œ HandlerMethod νƒ€μž…μΈμ§€ 체크
		if (handler instanceof HandlerMethod == false) {
			// return true이면 Controller에 μžˆλŠ” λ©”μ„œλ“œκ°€ μ•„λ‹ˆλ―€λ‘œ, κ·ΈλŒ€λ‘œ 컨트둀러둜 진행
			return true;
		}

		// 2.ν˜• λ³€ν™˜
		HandlerMethod handlerMethod = (HandlerMethod) handler;

		// 3. @Auth λ°›μ•„μ˜€κΈ°
		Auth auth = handlerMethod.getMethodAnnotation(Auth.class);
		Auth userRole = handlerMethod.getMethod().getDeclaringClass().getAnnotation(Auth.class);

		// 4. method와 class에 @Authκ°€ μ—†λŠ” 경우, 즉 인증이 ν•„μš” μ—†λŠ” μš”μ²­
		if (auth == null && userRole == null) {
			return true;
		}

		// 5. @Authκ°€ μžˆλŠ” κ²½μš°μ΄λ―€λ‘œ, μ„Έμ…˜μ΄ μžˆλŠ”μ§€ 체크
		HttpSession session = request.getSession();
		if (session == null) {
			// 둜그인 ν™”λ©΄μœΌλ‘œ 이동
			response.sendRedirect(request.getContextPath() + "/user/login/login_form");
			return false;
		}

		// 6. μ„Έμ…˜μ΄ μ‘΄μž¬ν•˜λ©΄ μœ νš¨ν•œ μœ μ €μΈμ§€ 확인
		Member authUser = (Member) session.getAttribute("loginUserinfo");
		if (authUser == null) {
			response.sendRedirect(request.getContextPath() + "/user/login/login_form");
			return false;
		}

		// 7. admin일 경우
		if (userRole != null) {
			String role = userRole.role().toString();
			if ("ADMIN".equals(role)) {
				if (!(authUser.getMemberStatus() == 9)) {
					response.sendRedirect(request.getContextPath());
					return false;
				}
			}
		} else if (auth != null) {
			String role = auth.role().toString();
			if ("ADMIN".equals(role)) {
				if (!(authUser.getMemberStatus() == 9)) {
					response.sendRedirect(request.getContextPath());
					return false;
				}
			}
		}

=> 인터셉터 상속 λ©”μ„œλ“œ κ΅¬ν˜„, μ„Έμ…˜ 유무 확인 및 μ—λ„ˆν…Œμ΄μ…˜ value 확인 및 κ΄€λ¦¬μž μ²˜λ¦¬κΉŒμ§€ μ΄ν•˜λŠ” κΈΈμ–΄μ„œ μƒλž΅ (λ‹€ 동일함)
 

μ΄λŸ°μ‹μœΌλ‘œ λ©”μ„œλ“œλ‚˜ ν΄λž˜μŠ€λ³„λ‘œ μ—λ„ˆν…Œμ΄μ…˜μ„ 달면 κΆŒν•œ 처리 끝!, 또 μ„Έμ…˜ λŒ€μ‹  AuthUser μ—λ„ˆν…Œμ΄μ…˜μœΌλ‘œ μ‚¬μš© κ°€λŠ₯ (νŒŒλΌλ―Έν„° retention이기 λ•Œλ¬Έμ΄λ‹€)

 
localhostμ—μ„œ μΌμ–΄λ‚˜λŠ” μž”μž”ν•œ μ—λŸ¬λ“€μ„ 작고 λ‚˜μ„œ war 파일둜 κ°•μ‚¬λ‹˜κ»˜ 전달을 λ“œλ Έλ‹€. 이 ν›„ μ„Έλ²ˆ 정도 μˆ˜μ •μ„ ν–ˆλŠ”λ° 잘 λ˜λŠ”μ§€λ₯Ό λͺ¨λ₯΄κ² λ‹€. μˆ˜μ •ν•  λ•Œλ§ˆλ‹€ λ˜λŠ”κ²Œ μ•ˆ 되고 μ•ˆ 되던게 λ˜λŠ”κ²Œ 도톡 이해가 μ•ˆλœλ‹€.. μ–˜λ₯Ό λ“€μ–΄ 지도 apiλ₯Ό 도메인 μˆ˜μ •μ„ ν–ˆλŠ”λ° 또 μ•ˆ λ˜λŠ”κ±Έ λ³΄λ‹ˆκΉŒ 어이가 μ—†λ‹€. 와λ₯΄ 파일 λ§Œλ“€ λ•Œλ§ˆλ‹€ keyλ₯Ό μž¬λ°œκΈ‰μ„ ν•΄μ•Ό ν•˜λŠ”κ±΄μ§€..
 

β–Ά νŒŒμ΄λ„ ν”„λ‘œμ νŠΈλ₯Ό 마친 ν›„ μ†Œκ°

이번 첫 μŠ€ν”„λ§μ„ μ΄μš©ν•œ ν”„λ‘œμ νŠΈλ₯Ό μ§„ν–‰ν•˜λ©° λŠλ‚€ 것은 아직도 μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” λŠ₯λ ₯이 ν„± 없이 λΆ€μ‘±ν•˜λ‹€ λŠκΌˆλ‹€. λ‚˜λ¦„ 객체지ν–₯을 μ œλŒ€λ‘œ κ³΅λΆ€ν•˜κ³  MVC νŒ¨ν„΄μ„ μ μš©ν•œ μžλ°”ν”„λ‘œμ νŠΈλ„ μ§„ν–‰ν–ˆκΈ°μ— μžμ‹ κ°μ΄ 생겨 μœ μ§€λ³΄μˆ˜κ°€ μ‰¬μš΄ ν΄λ¦°ν•œ μ½”λ“œλ₯Ό μž‘μ„±ν•˜μž μƒκ°ν–ˆμ§€λ§Œ 슀슀둜의 κΈ°λŒ€μ— 많이 λ―ΈμΉ˜μ§€ λͺ»ν•œ ν”„λ‘œμ νŠΈμ˜€λ‹€. 더 μ •μ§„ν•΄μ•Όκ² λ‹€λŠ” 생각이 아직도 λ“ λ‹€. 
λ§ˆμ§€λ§‰μœΌλ‘œ νŒ€μ›λ“€μ—κ²Œ λ„ˆλ¬΄ κ°μ‚¬ν–ˆλ‹€. ν˜Όμžμ˜€λ‹€λ©΄ 이 μ •λ„λ‘œ μ‹ μ†ν•˜κ²Œ ν•΄κ²°ν•˜μ§€ λͺ»ν–ˆλ˜ λ¬Έμ œλ„ 쀑간 쀑간 λͺ…μΎŒν•œ 해닡듀을 던져쀘 λ„ˆλ¬΄ κ³ λ§ˆμ› λ‹€. νŒ€ 과제λ₯Ό μ§„ν–‰ν•˜λ©΄μ„œλ„ ν˜• λˆ„λ‚˜λ“€μ΄ νŒ€μ›λ“€μ—κ²Œ λŒ€ν•˜λŠ” νƒœλ„λ₯Ό 제일 막내인 λ‚΄κ°€ μΈκ°„μ μœΌλ‘œλ„ λ°°μš°λŠ”κ²Œ λ§Žμ•˜λ‹€. κ°μ‚¬ν•©λ‹ˆλ‹€ λͺ¨λ‘!
 
많이 λ―Έμ•½ν•˜μ§€λ§Œ μ½”λ“œλ₯Ό λ³΄μ‹œλ €λ©΄ μ—¬κΈ°λ‘œ!
https://github.com/GroovyArea/healthJava-Spring-Project

λ°˜μ‘ν˜•