반응형
반응형
public String correctEnglishTypo(TypoVo typoVo) {

    List<String> correctedWords = new ArrayList<>();
    int choCount = 0; int joongCount = 0; int jongCount = 0; 

    String englishTypo = typoVo.getKeyword(); //"DnlsehDNDptJQKFMStkrwp";
    String koreanLetters = convertEnglishToKorean(englishTypo); // 변환된 한글 자/모 집합


    /** 초성 / 중성 / 종성 분리 **/
    for(int i = 0; i < koreanLetters.length(); i++) {

        boolean isCurrentJoong = false; // 중성
        boolean isChoJoong = false;

        String currentLetter = String.valueOf(koreanLetters.charAt(i)); // 현) 자/모

        String nextLetter = "";       // 다음)  자/모
        String secondNextLetter = ""; // 다다음) 자/모

        int beforeIndex = i - 1;
        int nextIndex = i + 1;
        int secondNextIndex = i + 2;


        /** 마지막인덱스 -1 범위에 속할 경우만 다음 값을 가져온다 **/
        if(isWithinBounds(i + 1, koreanLetters.length())) {
            nextLetter = String.valueOf(koreanLetters.charAt(i + 1));
        }

        /** 마지막인덱스 -2 범위에 속할 경우만 다음 값을 가져온다 **/
        if(isWithinBounds(i + 2, koreanLetters.length())) { 
            secondNextLetter = String.valueOf(koreanLetters.charAt(i + 2));
        }


        /** 중성 체크 **/
        for(String JOONG : Korean.JOONGS) {

            isCurrentJoong = (currentLetter.equals(JOONG))? true : false;

            /** (초성 + 중성)의 단어인지 체크 **/
            if(isCurrentJoong) {

                isChoJoong = checkChoJoong(nextLetter, secondNextLetter);

            }

            /** (초성 + 중성)의 단어일 경우 해당 단어 추가 후 초기화  **/
            if(isChoJoong && isCurrentJoong) {
                correctedWords.add(koreanLetters.substring(beforeIndex, nextIndex));

                choCount = 0; joongCount = 0; jongCount = 0;
            }

            /** (초성 + 중성 + 종성)의 단어일 경우  **/
            if(isChoJoong == false && isCurrentJoong) {
                joongCount = 1;
                break;
            }

        }


        /** 종성 체크 **/
        for(String jong : Korean.JONGS) {

            // 종성에 포함되는가?
            boolean isContainsJong = (currentLetter.equals(jong))? true : false;

            // 종성에 포함되면서 초성이 나온 경우는 종성 [초성과 종성의 겹치는 단어가 존재하기 때문에 체크 필요]
            boolean isJong = (choCount == 1 && isContainsJong) ? true : false;

            if(isJong) { jongCount = 1; break; }
        }


        /** 초성 체크 **/
        for(String cho : Korean.CHOS) {

            // 초성에 포함되는가?
            boolean isContainsCho = (currentLetter.equals(cho))? true : false;

            // 초성에 포함되면서 초성이 한번도 안 나온 경우는 초성 [초성과 종성의 겹치는 단어가 존재하기 때문에 체크 필요]
            boolean isCho = (choCount == 0 && joongCount == 0 && isContainsCho) ? true : false;

            if(isCho) { choCount = 1; break; }
        }




        /** 초성 + 중성 + 종성의 단어인 경우 조합 **/
        if((choCount == 1 && joongCount == 1 && jongCount == 1)) {

            correctedWords.add(koreanLetters.substring(i - 2, i + 1)); // [초 + 중 + 종] 값 가져오기
            choCount = 0; joongCount = 0; jongCount = 0;               // 한글자가 완성되어 초기화

        }


        /** 마지막 글자인 경우 (초성 + 중성) **/
        boolean isLastChoJoong = nextIndex == koreanLetters.length() 
                              && secondNextLetter.equals("") 
                              && nextLetter.equals("") 
                              && isCurrentJoong == true 
                              && choCount == 1 && joongCount == 1 && jongCount == 0 ? true : false; 

        /** 마지막 글자인 경우 (초성 + 중성 + 종성) **/
        boolean isLastChoJoongJong = secondNextIndex == koreanLetters.length() 
                                  && secondNextLetter.equals("") 
                                  && isCurrentJoong == true 
                                  && choCount == 1 && joongCount == 1 && jongCount == 0 ? true : false;



        if (isLastChoJoong) {
            correctedWords.add(koreanLetters.substring(beforeIndex, nextIndex));
            break;
        }else if (isLastChoJoongJong) {
            correctedWords.add(koreanLetters.substring(beforeIndex, secondNextIndex));
            break;
        }



    }

    StringBuilder words = new StringBuilder();

    /** 자/모 합쳐서 단어 생성 **/
    for(String correctWord : correctedWords) {

        int cho = 0; int joong = 0; int jong = 0;

        /** 한글자 자/모 **/
        for(int i = 0; i < correctWord.length(); i++) {

            String letter = Character.valueOf(correctWord.charAt(i)).toString();

            if(i == 0) cho = (int) Korean.CHO_CONVERT_MAP.get(letter);
            if(i == 1) joong = (int) Korean.JOONG_CONVERT_MAP.get(letter);
            if(i == 2) jong = (int) Korean.JONG_MAP.get(letter);
        }

        // 자/모 합치기 공식
        char word = (char) ((cho * 21 + joong) * 28 + jong + Korean.HANGUL_BASE); 
        words.append(word);

    }

    return words.toString();

    // 1. 한글자씩 나눈다 (종성인 경우 분리)
    // 2. 계산식으로 합친 한글자로 변환
    // 3. 한글자 변환된 항목 합치기

    // * 완전 이상한 오타인 경우 무시 정상적인 오타만 취급

//		char a = ((초성 * 21 + 중성) * 28 + (종성) + 0xAC00)
//		char a = ((3 * 21 + 8) * 28 + 4 + 0xAC00);

//		System.out.println(a);
}



/**
 * @param nextLetter       - 다음 자/모
 * @param secondNextLetter - 다다음 자/모
 * @return 초성/중성 여부
 */
private boolean checkChoJoong(String nextLetter, String secondNextLetter) {

    boolean isChoJoong = false;

    /** 종성 체크 (초성 + 중성으로 끝나는 단어인지 체크) **/
    for(String JONG_CHAR : Korean.JONGS) {

        // 두번째 자/모가 중성인가?
        boolean isSecondNextJoong = checkJoong(secondNextLetter); 

        // 다음 자/모가 종성인가?
        boolean isNextJong = (nextLetter.equals(JONG_CHAR))? true : false;

        if(isNextJong == false && isSecondNextJoong) {

            isChoJoong = true;
            break;
        }
    }
    return isChoJoong;
}


/**
 * @param  secondNextLetter - 다다음 자/모
 * @return 다다음 자/모 중성 여부
 */
private boolean checkJoong(String secondNextLetter) {

    boolean isSecondNextJoong = false;

    for(String Joong : Korean.JOONGS) {
        isSecondNextJoong = (secondNextLetter.equals(Joong))? true : false;
        if(isSecondNextJoong) break;
    }

    return isSecondNextJoong;
}



/**
 * @param  index  - 현재 인덱스
 * @param  length - Array 및 String 인덱스의 전체 크기
 * @return 정상 범위 체크 (out of bounds 체크)
 */
private boolean isWithinBounds(int index, int length) {
    return index >= 0 && index < length;
}

/**
 * @param  englishTypo - 영문 오타
 * @return 영문 오타를 한글로 변환 (rk → ㄱㅏ)
 */
private String convertEnglishToKorean(String englishTypo) {

    StringBuilder korean = new StringBuilder(englishTypo);
    String volew = "";

    /** 이중모음 변환 **/
    for (Map.Entry<String, String> entry : Korean.DOUBLE_VOWELS_KOREAN_MAP.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        korean.replace(0, korean.length(), korean.toString().replace(key, value));
    }

    /** 단모음 변환 **/
    for (Map.Entry<String, String> entry : Korean.SINGLE_VOWELS_KOREAN_MAP.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        korean.replace(0, korean.length(), korean.toString().replace(key, value));
    }


    /** 쌍자음 변환 **/
    for (Map.Entry<String, String> entry : Korean.DOUBLE_CONSONANTS_KOREAN_MAP.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();

        int isContain = korean.indexOf(key);

        // 다다음 글자가 모음이 아니여야 쌍자음 변환
        if(isContain != -1) {

            try {volew = String.valueOf(korean.charAt(isContain + 2));}
            catch(Exception e) {volew = String.valueOf(korean.charAt(isContain + 1));}

            boolean hasNextVolew = Pattern.matches(koreanRegex, volew);

            if(!hasNextVolew) korean.replace(0, korean.length(), korean.toString().replace(key, value));

        }


    }


    /** 단자음 변환 **/
    for (Map.Entry<String, String> entry : Korean.SINGLE_CONSONANTS_KOREAN_MAP.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        korean.replace(0, korean.length(), korean.toString().replace(key, value));
    }


    return korean.toString();
}
반응형
반응형
import java.io.IOException;

import org.springframework.core.io.ClassPathResource;

public class test {

	public static void main(String[] args) throws IOException {

		ClassPathResource resource = new ClassPathResource("properties.ini");

		System.out.println("resource.getFile() : " + resource.getFile());
		System.out.println("resource.getFilename() : " + resource.getFilename());
		System.out.println("resource.getInputStream() : " + resource.getInputStream());
		System.out.println("resource.getPath() : " + resource.getPath());
		System.out.println("resource.getURL() : " + resource.getURL());
		System.out.println("resource.getURI() : " + resource.getURI());

		/**
		 * 
		 	resource.getFile() : F:\project\normal_spring_project\workspace\sample\target\classes\properties.ini
		 	resource.getFilename() : properties.ini
			resource.getInputStream() : java.io.BufferedInputStream@5ebec15
			resource.getPath() : properties.ini
			resource.getURL()) : file:/F:/project/normal_spring_project/workspace/sample/target/classes/properties.ini
			resource.getURI() : file:/F:/project/normal_spring_project/workspace/sample/target/classes/properties.ini
		 * 
		 */
		
	}

}

Spring에서 쉽게 가져올 수 있도록 제공해주는 라이브러리[ClassPathResource]로 쉽게 가져올 수 있습니다

가져올 경로는 src/main/resources가 기본 경로입니다

 

🔗참고 및 출처 

http://june0313.github.io/2018/08/11/read-resource-file-on-spring/

 

반응형
반응형

 

 

이클립스 폴더 안에 eclipse.ini 에 하기 내용을 추가한다.

-Dfile.encoding=UTF-8

반응형
반응형
package com.lsj.blog;

import java.util.Random;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.lsj.blog.model.RoleType;
import com.lsj.blog.repository.UserRepository;
import com.lsj.dto.ResponseDto;

@RestController
public class DummyController {

	@Autowired // DI
	private UserRepository userRepository;
	
	@PostMapping("/api/user")
	@Transactional
	public ResponseDto<String> save(@RequestBody BlogUser user){

		BlogUser usre2 = new BlogUser();
		
		Random random = new Random();

		usre2.setUsername(String.valueOf(random.nextInt(10)));
		usre2.setPassword("12345"); 
		usre2.setEmail("abc@naver.com");
		userRepository.save(usre2); 
			
		new ResponseDto<String>(HttpStatus.OK.value(), "데이터 insert에 성공했습니다.");
		// HttpStatus.OK.value() → 200이라는 코드 반환 (정상 성공)
	}
}
package com.lsj.blog.handler;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;

import com.lsj.dto.ResponseDto;

@ControllerAdvice // 어떤 Exception이 오든 이 페이지로 오게 된다.
@RestController
public class GlobalExceptionHandler {
	
	@ExceptionHandler(value=Exception.class) // 모든 부모 Exception
	public ResponseDto<String> handleException(Exception e) {
		return new ResponseDto<String>(HttpStatus.INTERNAL_SERVER_ERROR.value(),e.getMessage());
		// HttpStatus.INTERNAL_SERVER_ERROR.value() → 500 코드 반환 (서버 에러)
		//return "<h1>" + e.getMessage() + "</h1>";
	}
}
package com.lsj.dto;

import org.springframework.http.HttpStatus;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ResponseDto<T> {
	int status;
//	HttpStatus status;
	T data;
}

 

반응형
반응형

📝텍스트 파일로만 페이지 띄우기

tomcat path : C:\tomcat9
server.xml path : C:\tomcat9\conf\server.xml

<Host name="localhost"  appBase="webapps" unpackWARs="true" autoDeploy="true">
    <Context path="it" docBase="C:\tomcat9\webapps\ITWeb" privileged="true"/>
    ....


페이지에 띄울 텍스트파일 경로 : C:\tomcat9\webapps\ITWeb\nana.txt
nana.txt 내용 : 안녕하세요
페이지 호출 URL : http://localhost:8080/it/nana.txt

 

 

📝클래스 파일 직접 만들어서 페이지 띄우기

test.java 

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class test extends HttpServlet {
 public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
 {
  System.out.println("hello Servlet");
 }
}
1. cmd test.java 있는 경로로 이동
2. class 파일 만들기 → javac -cp C:\tomcat9\lib\servlet-api.jar test.java

3. 호출하기 위한 설정파일 수정 (web.xml) [WEB-INF안에 있는 파일은 직접 요청이 불가능하다.]
   WEB-INF 호출하기 위한 설정 파일 경로 : C:\tomcat9\webapps\ROOT\WEB-INF\web.xml
   실행시킬 class파일 : C:\tomcat9\webapps\ROOT\WEB-INF\classes\test.class

web.xml 내용

<servlet>
<servlet-name>testing</servlet-name>
<servlet-class>test</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>testing</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>


→
hello라는 url 요청이 오면 testing이라는 서블릿의 이름을 가진 애를 실행시키라는 의미
testing이라는 이름을 가지는 서블릿의 클래스파일은 test이다


이렇게 매핑정보를 xml으로 할수도 있지만(2.x) annotation(3.x)으로도 사용 가능하다.

annotation 방식을 이용하고 싶을 경우 metadata-complete를 false로 바꿔야한다.
경로 : C:\tomcat9\webapps\ROOT\WEB-INF\web.xml

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  version="4.0"
  metadata-complete="false">



반응형
반응형

 

package runnable_jar;

import java.text.SimpleDateFormat;
import java.util.Date;

public class ThreadGroupExample {

	public static void main(String[] args) {

		Date today = new Date();
		SimpleDateFormat yyyymmddForamt = new SimpleDateFormat("yyyyMMdd");
		
		String todayDate = yyyymmddForamt.format(today); 
		String date = "20221201";
		
		System.out.println("todayDate : " + todayDate); // 20221201
		
		int compare = todayDate.compareTo(date);

		// A.compareTo(B) 
		// A = B  → 0
		// A < B  → -1
		// A > B  → 1
		
		if     (compare > 0) System.out.println("date 날짜는 오늘 날짜 이전입니다."); 
		else if(compare < 0) System.out.println("date 날짜는 오늘 날짜 이후입니다.");
		else                 System.out.println("date 날짜는 오늘 날짜와 동일합니다.");
	}

}

// 출처 : https://soraji.github.io/java/2019/04/15/compareDATE/
package runnable_jar;

import java.text.SimpleDateFormat;
import java.util.Date;

public class ThreadGroupExample {

	public static void main(String[] args) {

		Date today = new Date();
		SimpleDateFormat yyyymmddForamt = new SimpleDateFormat("HHmmss");
		
		String todayTime = yyyymmddForamt.format(today); 
		String time = "224246";
		
		System.out.println("todayTime : " + todayTime); // 20221201
		
		int compare = todayTime.compareTo(time);

		// A.compareTo(B) 
		// A = B  → 0
		// A < B  → -1
		// A > B  → 1
		
		if     (compare > 0) System.out.println("time < 현재 시각"); 
		else if(compare < 0) System.out.println("time > 현재시각");
		else                 System.out.println("time = 현재시각");
	}

}
반응형
반응형

📝1. keysotre 파일 생성 (SSL인증서)

1. cmd 관리자 권한으로 실행
2. cd ${jdk/bin경로}
3. 하기 명령어 입력
keytool -genkey -alias tomcat -keyalg RSA -keystore tomcat.keystore
설명 : keytool -genkey -alias tomcat -keyalg RSA -keystore ${keystore 파일명}

4. 하기 내용 입력하기

키 저장소 비밀번호 입력:
새 비밀번호 다시 입력:
이름과 성을 입력하십시오.
[Unknown]: 
조직 단위 이름을 입력하십시오.
[Unknown]:
조직 이름을 입력하십시오.
[Unknown]: 
구/군/시 이름을 입력하십시오?
[Unknown]:
시/도 이름을 입력하십시오.
[Unknown]: seoul
이 조직의 두 자리 국가 코드를 입력하십시오.
[Unknown]: KR
N=maruara, OU=Unknown, O=Unknown, L=Unknown, ST=seoul, C=KR이(가) 맞습니까?
[아니오]: y
tomcat>에 대한 키 비밀번호를 입력하십시오.
(키 저장소 비밀번호와 동일한 경우 Enter 키를 누른다):

 

📝2. tomcat의 server.xml에서 https 설정하기

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystorePass="gkgkgk" keystoreFile="d:/openssl/tomcat.keystore"/>

port → https 통신 포트
keystorePass → 위에서 설정한 패스워드
keystoreFile → ssl 인증서 경로
반응형
반응형

📝 Mybatis 연결하기

<bean id="dataSource"
    class="org.apache.commons.dbcp2.BasicDataSource">
    <property name="driverClassName"
        value="com.mysql.cj.jdbc.Driver" />
    <property name="url"
        value="jdbc:mysql://localhost:3306/customer?characterEncoding=UTF-8" />
    <property name="username" value="root" />
    <property name="password" value="1234" />

</bean>

<bean id="sqlSessionFactory"
    class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation"
        value="classpath:mybatis/mybatis-config.xml" />
    <property name="mapperLocations"
        value="classpath:mybatis/mappers/**/*mapper.xml" />
</bean>
<!-- classpath란 src/main/java를 의미한다. classpath:com.mycompany.myapp -->



<bean id="sqlSession"
    class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg ref="sqlSessionFactory" />
</bean>

root-context.xml

<!-- Mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.2.8</version>
</dependency>

<!-- Mybatis-Spring -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.2.2</version>
</dependency>

pom.xml

package com.company.macro;

import java.util.List;

import com.company.macro.VO.CustomerVO;

public interface MybatisImpl {

	public String selectCount();
	public List<CustomerVO> selectAll();
	public void insertAll();
	
//	@Select("SELECT * FROM customer")
//	public String selectAll();
}

MybatisImpl.java

 

query문의 이름과 반환 타입을 선언합니다.

public class CustomerVO {
	private String customer_no;
	private String customer_nm;
	// DB에 있는 컬럼명과 VO와 동일하게 가야한다.

	
	public String getCustomer_no() {
		return customer_no;
	}
	public void setCustomer_no(String customer_no) {
		this.customer_no = customer_no;
	}
	public String getCustomer_nm() {
		return customer_nm;
	}
	public void setCustomer_nm(String customer_nm) {
		this.customer_nm = customer_nm;
	}
	
	
}

CustomerVO.java

 

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 상기 내용은 반드시 필요한 내용 -->
<configuration>
   <properties>
        <property name="mybatis 안에서 사용될 column의 이름" value="디비 컬럼명" />
        <property name="tblCustomer" value="customer" />
        <property name="customer_no" value="customer_no" />
        <property name="customer_nm" value="customer_nm" />
        
        <property name="tblMember" value="tbl_member"/>
        <property name="colUserid" value="userid"/>
        <property name="colPassword" value="password"/>
        <property name="colEmail" value="email"/>
    </properties>

    <typeAliases>
        <typeAlias alias="customerVO" type="com.company.macro.VO.CustomerVO" />
    </typeAliases>
    <!-- VO가 들어있는 패키지명  -->
</configuration>

webapps/resources/mybatis-config.xml

 

property는 mapper.xml에서 쓰이는

typeAliases는 mapper.xml의 select해서 나온 데이터를 VO에 바로 담을수 있게 사용하겠다는 의미입니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 상기 내용은 반드시 필요한 내용 -->

<!-- interface 명 -->
<mapper namespace="com.company.macro.mybatisImpl">

	<select id="selectCount" resultType="string">
		SELECT count(*) FROM customer
	</select>

	<select id="selectAll" resultType="customerVO">
		SELECT * FROM ${tblCustomer}
	</select>

</mapper>

webapps/resources/mappers/mapper.xml

 

resultType에는 typeAliases에 적은 VO를 사용할 수 있습니다.

${property name명} 으로 name에 해당하는 value를 가져올 수 있습니다.

 

 

 

📝 Mybatis 동작 구조

 

 

 

Mybatis 사용시 데이터 흐름

사용자 → 컨트롤러 → Servie(interface)[DI용 + 규격화] → ServiceImpl(class) → Dao(interface)[DI용 + 규격화] → mapper.xml(xml이지만 Dao에 설정된 interface를 따라감 implements한 class라고 생각하면 됨) → DB → 실행결과 → ServiceImpl → Controller → 사용자

 

Service → 비즈니스 로직 (DaoImpl을 통해 여러가지 쿼리문(Dao를 1개 이상 실행)을 가져오고 Java를 이용해 데이터를 만들어 반환 가능)

Dao       → DB Query 실행문이 들어감 (1개의 Query문)

mapper.xml → Query문에 대한 내용

Interface 사용 이유 → DI 하기 위함과 규격화를 위해 사용 → DI를 제대로 활용할 줄 몰라서 아직 와닿지 않음

 

* 참고 : 보통은 어떻게 처리하는지 모르겠지만 Service에 로직이 길어졌는데 해당 부분에 공통 로직이 있어서 (로그인 세션 체크 등...) 공통 API로 뺄 때 그 안에 Mapper를 이용해야하는 경우(DB Connection이 들어가는 로직)일 경우 해당 Util(공통 API)도 또한 Mapper를 Injection해서(Autowired) 사용할텐데 해당 Mapper를 이용하기 위해서는 @Component 또는 @Service 등 해당 클래스를 IoC컨테이너에 담아야한다.

 

LoginUtil util = new Util
LoginVo vo = util.getSessionInfo()
----------- (X)

@Autowired
LoginUtil loginUtil;

....

LoginVo vo = loginUtil.getSessionInfo();
----------- (O)

 

<!-- spring version과 spring-jdbc version은 동일해야한다 -->
<!-- mybatis, mybatis-spring, spring-version 서로 호환되는 버전으로해야한다. -->
<!-- 만약 호환 안 되는 버전 사용시 sqlSession 객체 만들 때 에러 나오는데 버전이 맞지 않습니다라 이런 에러도 안 나와서 에러 체킹이 힘듬 -->
<!-- root-context.xml에서 설정후 실행 시 sqlSession 객체 만들 때 정상 호환 버전들이면 정상 작동하거나 잘못 된 부분에 대한 정확한 에러가 나옴 -->


<properties>
    <java-version>11</java-version>
    <org.springframework-version>5.0.5.RELEASE</org.springframework-version>
    <org.aspectj-version>1.6.10</org.aspectj-version>
    <org.slf4j-version>1.6.6</org.slf4j-version>
</properties>
....

<!-- jdbc -->
<dependency>
  <groupId>org.mariadb.jdbc</groupId>
  <artifactId>mariadb-java-client</artifactId>
  <version>2.0.3</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>
<!-- Mybatis -->	
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
<!-- Mybatis + Spring -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.2</version>
</dependency>

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
	<bean id="dataSource"
	    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	    <property name="driverClassName"
	        value="org.mariadb.jdbc.Driver" />
	    <property name="url"
	        value="jdbc:mariadb://192.168.0.40:7001/ocr_search" />
	    <property name="username" value="testid" />
	    <property name="password" value="testpassword" />
	</bean>

	<bean id="sqlSessionFactory"
	    class="org.mybatis.spring.SqlSessionFactoryBean">
	    <property name="dataSource" ref="dataSource" />
        <property name="configLocation"
        value="classpath:mybatis-config.xml" />
    	<property name="mapperLocations"
     	   value="classpath:mappers/*Mapper.xml" />
	</bean>
	
	<!-- classpath란 src/main/java 또는 src/main/resources를 의미한다. -->
	<!-- classpath는 build path - source에 등록된 경로들이다.  -->
	<!-- classpath:mybatis-config.xml → Alias등 설정 하는 설정 파일 -->
	<!-- classpath:mappers/*Mapper.xml → Query를 적는 파일이다 -->
	<!-- src/main/java/mappers 폴더에 관리하겠다는 의미이고 파일명이 Mapper.xml로 끝나는 파일들을 등록하겠다는 의미이다. -->
	
	<bean id="sqlSession"
	    class="org.mybatis.spring.SqlSessionTemplate">
	    <constructor-arg ref="sqlSessionFactory" />
	</bean>
	
	<!-- mapper 어노테이션을 인식 시키기위해 필요 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	    <property name="basePackage" value="com.company.ocrsearch" />
	</bean>
		
</beans>

root-context.xml

 

SqlSession : DB 연결하기 위한 객체 및 Query 수행 역할 (SqlSessionFactory가 주입 됨)

SqlSessionFactory : mapper.xml에 대한 설정 및 mapper.xml(쿼리문) 읽을 객체

 

 

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

public class EngineDto {

	private String uuid = "";
	private String id = "";
	private String password = "";
	private String salt = "";
	private String fail_cnt ="";
	
	public String getUuid() {
		return uuid;
	}
	public void setUuid(String uuid) {
		this.uuid = uuid;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getSalt() {
		return salt;
	}
	public void setSalt(String salt) {
		this.salt = salt;
	}
	public String getFail_cnt() {
		return fail_cnt;
	}
	public void setFail_cnt(String fail_cnt) {
		this.fail_cnt = fail_cnt;
	}
	
	public String toStringShortPrefix() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
	
	
}

EngineDto.java

→ 데이터가 담기는 부분이다.

 

 

---- String 단일 매개변수 동적 쿼리 ----


import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.company.ocrsearch.engine.dto.EngineDto;
import com.company.ocrsearch.engine.service.EngineService;

import global.configuration.Config;
import global.dto.DirectoryPathDto;
import global.dto.ServerInfoDto;


@Controller
public class EngineAPI {
	
	@Autowired
	@Qualifier("EngineSerive")
	EngineService engineService;
	
	@RequestMapping(value = "/myabtis-test", method = RequestMethod.GET)
	public void testMybatis(String uuid) throws Exception{
		
		EngineDto engineDto = engineService.getUserInfo(uuid);
		System.out.println(engineDto.toStringShortPrefix());
		
	}
	
}

EngineAPI.java

→ Controller로 사용자의 요청이 들어오는 부분이다.

 


import com.company.ocrsearch.engine.dto.EngineDto;

public interface EngineService {
	
	public EngineDto getUserInfo(String uuid) throws Exception;
}

EngineSerivce.java

→ 비즈니스 로직이 들어가며 규격화 및 DI 역할

 

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.company.ocrsearch.engine.dao.EngineDao;
import com.company.ocrsearch.engine.dto.EngineDto;
import com.company.ocrsearch.engine.service.EngineService;

@Service("EngineSerive")
public class EngineServiceImpl implements EngineService{

	@Autowired
	private EngineDao engineDao;
	
	@Override
	public EngineDto getUserInfo(String uuid) throws Exception {
		
		EngineDto engineDto = engineDao.selectUser(uuid);
		return engineDto;
	}

}

EngineServiceImpl.java

→ 실제 비즈니스 로직 구현한 것으로 DB 쿼리문을 사용할 경우 1개 이상의 Dao로 구성되어 있다.

받아온 쿼리문을 가지고 여러 동작을 하거나 여러개의 Dao를 호출해서 결과를 조합해서 Dto에 담아서 내려주는 형태

 

 

 

 

package com.company.ocrsearch.engine.dao;

import org.apache.ibatis.annotations.Mapper;

import com.company.ocrsearch.engine.dto.EngineDto;

@Mapper
public interface EngineDao {
	public EngineDto selectUser(String uuid) throws Exception;
	
}

EngineDao.java

→ root-context.xml에서 설정한 org.mybatis.spring.mapper.MapperScannerConfigurer에 포함된 패키지일 경우 @mapper 어노테이션을 찾아서 *mapper.xml과 연결해준다. interface이기 때문에 규격화및 DI 역할을 한다.

 

 

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.company.ocrsearch.engine.dao.EngineDao">

	<select id="selectUser" parameterType="String"
		resultType="com.company.ocrsearch.engine.dto.EngineDto">
			SELECT
			        ID          -- 아이디
			      , FAIL_CNT    -- 로그인 실패 횟수
			 FROM   admin_user	
			 WHERE 
			        uuid = #{UUID}
	</select>
</mapper>

EngineInfoMapper.xml 

→  쿼리문이 여기에 작성되며 연결할 interface명을 mapper namespace에 적는다.

xml을 EngineDao inteface를 implements한 class라고 생각하면 된다. 

 

id → Dao Interface에서 구현해야할 Funciton Name을 의미한다.

parameterType → 동적 쿼리를 위해 넘겨주는 파라미터를 의미한다. #{UUID}에 들어가는 파라미터는 String이 된다.

[ String 뿐만 아니라 HashMap 등이 있기 때문에 상황에 맞게 쓰면 된다 java.util.HashMap 등...)

String의 경우 HashMap의 경우 키 값으로 들어가기 때문에

resultType → 결과가 담길 곳을 의미한다. 일반적으로 여러개의 결과이고 데이터의 이동은 Dto에 담아서 하기 때문에 Dto를 적어줬다.

너무 당연한 이야기지만 내려받을 필드명과 Dto 필드는 같아야한다. (Alias 걸어서 Dto 수정 없이 Dto에 맞출 수도 있음)

[ String 뿐만 아니라 HashMap 등이 있기 때문에 상황에 맞게 쓰면 된다 java.util.HashMap 등...)

 

* 상속받는 놈(mapper.xml)이 ParameterType을 받기로 했기 때문에 Dao (interface)에도 그렇게 설정해줘야한다.

→ 매개변수가 1개의 String 이여야 함 (String의 경우 이름(#{UUID})을 정할 수 없기 때문에 들어온 그대로 들어가게 된다.)

     여러개의 매개변수를 받으려면 HashMap이나 Dto가 적당하다.

타입은 신경 안 쓰는 것 같다. → DB 필드가 문자 타입이여도 parameterType=int로 보내줘도 인식함

 

 

 

 

---- HashMap 매개변수 동적 쿼리 ----

 

pom.xml, root-context.xml, DTO는 동일합니다.

 

import java.util.HashMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.company.ocrsearch.engine.dto.EngineDto;
import com.company.ocrsearch.engine.service.EngineService;


@Controller
public class EngineAPI {
	
	@Autowired
	@Qualifier("EngineSerive")
	EngineService engineService;
	
	@RequestMapping(value = "/mybatis-test", method = RequestMethod.GET)
	public void testMybatis(String uuid, String userId) throws Exception{
		
		HashMap<String, String> data = new HashMap<>();
		data.put("UUID", uuid); // mapper에서 대소문자 구분합니다.
		data.put("userId", userId);
		
		
		EngineDto engineDto = engineService.getUserInfo(data);
		System.out.println(engineDto.toStringShortPrefix());
		
	}
	
}

EngineAPI.java

 

import java.util.HashMap;

import com.company.ocrsearch.engine.dto.EngineDto;

public interface EngineService {
	
	public EngineDto getUserInfo(HashMap<String, String> uuid) throws Exception;
}

EngineService.java

 

import java.util.HashMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.company.ocrsearch.engine.dao.EngineDao;
import com.company.ocrsearch.engine.dto.EngineDto;
import com.company.ocrsearch.engine.service.EngineService;

@Service("EngineSerive")
public class EngineServiceImpl implements EngineService{

	@Autowired
	private EngineDao engineDao;
	
	@Override
	public EngineDto getUserInfo(HashMap<String, String> uuid) throws Exception {
		
		EngineDto engineDto = engineDao.selectUser(uuid);
		return engineDto;
	}
}

EngineServiceImpl.java

 

import java.util.HashMap;

import org.apache.ibatis.annotations.Mapper;

import com.company.ocrsearch.engine.dto.EngineDto;

@Mapper
public interface EngineDao {
	public EngineDto selectUser(HashMap<String, String> uuid) throws Exception;
	
}

EngineDao.java

 

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.company.ocrsearch.engine.dao.EngineDao">

	<select id="selectUser" parameterType="java.util.HashMap"
		resultType="com.company.ocrsearch.engine.dto.EngineDto">
			SELECT
			        ID          -- 아이디
			      , FAIL_CNT    -- 로그인 실패 횟수
			 FROM   admin_user	
			 WHERE  uuid = #{UUID}
			   AND  id   = #{userId}
	</select>
</mapper>

EngineInfoMapper.xml

달라진 점은 HashMap을 파라미터로 넘긴다는 점입니다.

키의 값을 기준으로 동적쿼리가 생성되며 대소문자를 구분하니 주의하시길 바랍니다.

 

 

 

---- Dto 매개변수 동적 쿼리 ----

 

pom.xml, root-context.xml, DTO는 동일합니다.

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.company.ocrsearch.engine.dto.EngineDto;
import com.company.ocrsearch.engine.service.EngineService;


@Controller
public class EngineAPI {

	@Autowired
	@Qualifier("EngineSerive")
	EngineService engineService;
	
	@RequestMapping(value = "/mybatis-test", method = RequestMethod.GET)
	public void testMybatis(EngineDto requestEngineDto) throws Exception{

		EngineDto engineDto = engineService.getUserInfo(requestEngineDto);
		System.out.println(engineDto.toStringShortPrefix());
		
	}
	
}

EngineAPI.java

 

 

import com.company.ocrsearch.engine.dto.EngineDto;

public interface EngineService {
	
	public EngineDto getUserInfo(EngineDto requestEngineDto) throws Exception;
}

EngineService.java

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.company.ocrsearch.engine.dao.EngineDao;
import com.company.ocrsearch.engine.dto.EngineDto;
import com.company.ocrsearch.engine.service.EngineService;

@Service("EngineSerive")
public class EngineServiceImpl implements EngineService{

	@Autowired
	private EngineDao engineDao;
	
	@Override
	public EngineDto getUserInfo(EngineDto requestEngineDto) throws Exception {
		
		EngineDto engineDto = engineDao.selectUser(requestEngineDto);
		return engineDto;
	}
}

EngineServiceImpl.java

 

import org.apache.ibatis.annotations.Mapper;

import com.company.ocrsearch.engine.dto.EngineDto;

@Mapper
public interface EngineDao {
	public EngineDto selectUser(EngineDto requestEngineDto) throws Exception;
}

EngineDao.java

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.company.ocrsearch.engine.dao.EngineDao">

	<select id="selectUser" parameterType="com.company.ocrsearch.engine.dto.EngineDto"
		resultType="com.company.ocrsearch.engine.dto.EngineDto">
			SELECT
			        ID          -- 아이디
			      , FAIL_CNT    -- 로그인 실패 횟수
			  FROM  admin_user	
			 WHERE  uuid = #{uuid}
			   AND  id   = #{id}
	</select>
</mapper>

mapper.xml

 

 

 

 

 

https://wildeveloperetrain.tistory.com/26

 

@Component 와 @Bean, @Autowired 어노테이션 알아보기

자주 사용하면서도 정확하게 무슨 용도로 사용되는지, 어떤 동작 원리를 가지는지, 어떤 차이가 있는지 잘 몰랐던 부분에 대해서 공부합니다. 먼저 Annotation에 대해서 간단하게 알아보겠습니다.

wildeveloperetrain.tistory.com

 

반응형
반응형

 

<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.17.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.17.1</version>
</dependency>

 

 

<!-- 경로 : main/resources/log4j2.xml -->
<?xml version="1.0" encoding="UTF-8"?>

<configuration status="DEBUG">

	<!-- 콜솔 -->
	<Console name="console" target="SYSTEM_OUT">
		<PatternLayout
			pattern="%d{yyyy-MM-dd hh:mm:ss} %5p [%c] %m%n" />
	</Console>

	<Appenders>
		<!-- 파일 -->
		<RollingFile name="as_api_param">
			<FileName>/home/Search/logs/search/search_param.log</FileName>
					<FilePattern>/home/Search/logs/search/search_param_%d{yyyy-MM-dd}.log
			</FilePattern>
			<PatternLayout>
				<Pattern>%d{yyyy-MM-dd HH:mm:ss} %5p [%c] %m%n</Pattern>
			</PatternLayout>
			<Policies>
				<TimeBasedTriggeringPolicy interval="1"
					modulate="true" />
			</Policies>
		</RollingFile>

		<RollingFile name="as_api_uri">
			<FileName>/home/Search/logs/search/search_uri.log</FileName>
			<FilePattern>/home/Search/logs/search/search_uri_%d{yyyy-MM-dd}.log</FilePattern>
			<PatternLayout>
				<Pattern>%d{yyyy-MM-dd HH:mm:ss} %5p [%c] %m%n</Pattern>
			</PatternLayout>
			<Policies>
				<TimeBasedTriggeringPolicy interval="1"
					modulate="true" />
			</Policies>
		</RollingFile>
	</Appenders>

	<loggers>
		<Logger name="AS_API_PARAM" level="DEBUG" additivity="false">
			<AppenderRef ref="as_api_param" />

		</Logger>
		<Logger name="AS_API_URI" level="DEBUG" additivity="false">
			<AppenderRef ref="as_api_uri" />
		</Logger>
		<Logger name="CONSOLE" level="DEBUG" additivity="false">
			<AppenderRef ref="console"/>
		</Logger>
	</loggers>


</configuration>

Console → Console에 출력

RollingFile → 로그 찍는 형식 (Rolling이란 파일을 계속 갱신하는 것을 의미)
name → 로그 찍는 형식의 이름 (AppenderRef에서 사용)

FileName → 로그 저장경로와 로그 이름
FilePattern → Rolling시 저장될 위치및 로그파일 이름
PatternLayout  → 로그에 출력할 로그 내용 형식
%d{yyyy-MM-dd HH:mm:ss} → yyyy-MM-dd HH:mm:ss 로그 찍힌 시각
%5p → 로그형식
%C → 클래스명 표시
%m → 로그 출력 본 내용
%n → 각 플랫폼에 맞는 개행



TimeBasedTriggeringPolicy → 로그에 사용할 정책
interval → interval 단위로 로그 파일 새로 생성 (롤오버라고 한다)
FilePattern 에 적인 형식이 yyyy-MM-dd일 경우  interval=1 기준 1일 단위
yyyy-MM-dd-hh 일 경우는 1시간 단위
moulate → false인 경우 application 시작 시각으로부터가 기준이고 true인 경우 정각인 시각마다 롤오버가 일어난다.

Loggers → 위에 선언한 로그 형식중 사용할 로그 형식 선언
Logger 
 - name → java파일에서 사용할 Log의 이름
 - level → 로그 Level 해당 로그 이상만 로그 적재
 - addtivity → 중복 로깅 여부 (RollingFile name이 여러 AppenderRef ref에서 참조될 시 한번만 찍게 된다.)

package com.company.AS_API;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.bind.annotation.RequestMapping;




@Controller
public class companyController {
	
	@RequestMapping(value = "/logs", method = RequestMethod.GET)
	public void makeLogs {
		
		Logger console = LogManager.getLogger("CONSOLE");
		Logger APIParamLogger = LogManager.getLogger("AS_API_PARAM");
		Logger APIURILogger = LogManager.getLogger("AS_API_URI");

		// 로그 찍기
		APIParamLogger.debug("debug 입니다.");
		APIParamLogger.info("info 입니다.");
		APIParamLogger.warn("warn 입니다.");
		APIParamLogger.error("error 입니다.");
		APIParamLogger.fatal("fatal 입니다.");
	}
}

 

 

 

─── 참고 RollingFile + Console ── 

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

	<Appenders>
		<!-- 콜솔 -->
		<Console name="console" target="SYSTEM_OUT">
			<PatternLayout
				pattern="%d{yyyy-MM-dd hh:mm:ss} %5p [%c] %m%n" />
		</Console>
		<RollingFile name="admin_real">
			<FileName>/home/Search/logs/admin/AA.log</FileName>
			<FilePattern>/home/Search/logs/admin/AA_%d{yyyy-MM-dd}.log</FilePattern>
			<PatternLayout>
				<Pattern>%d{yyyy-MM-dd HH:mm:ss} %5p [%c] %m%n</Pattern>
			</PatternLayout>
			<Policies>
				<TimeBasedTriggeringPolicy interval="1"
					modulate="true" />
			</Policies>
		</RollingFile>
		<RollingFile name="admin_local">
			<FileName>D://logs//aa.log</FileName>
			<FilePattern>D://logs//aa_%d{yyyy-MM-dd}.log</FilePattern>
			<PatternLayout>
				<Pattern>%d{yyyy-MM-dd HH:mm:ss} %5p [%c] %m%n</Pattern>
			</PatternLayout>
			<Policies>
				<TimeBasedTriggeringPolicy interval="1"	modulate="true" />
			</Policies>
		</RollingFile>
	</Appenders>

	<loggers>
		<Logger name="ADMIN_LOGGER_LOCAL" level="DEBUG"
			additivity="false">
			<AppenderRef ref="console" />
			<AppenderRef ref="admin_local" />
		</Logger>

		<Logger name="ADMIN_LOGGER_REAL" level="DEBUG"
			additivity="false">
			<AppenderRef ref="console" />
			<AppenderRef ref="admin_real" />
		</Logger>
		
		
	</loggers>


</configuration>

 

 

형식 참고 블로그 : http://changpd.blogspot.com/2013/05/spring-lo4j.html

반응형