1. Keyboard package

1.1 사용한 패키지

Windows Keyboard를 제어할 목적으로 Python의 Keyboard 패키지를 사용하였다.

https://github.com/boppreh/keyboard

 

boppreh/keyboard

Hook and simulate global keyboard events on Windows and Linux. - boppreh/keyboard

github.com

 

1.2 사용목적

한 손으로 사용하기 힘든 단축키를 한손으로만 사용하기 위해 Keyboard Pacakge에서의 Hotkey를 사용하였다.

예를 들어, 윈도우 데스크톱 이동 명령어인 ctrl+window key+left or right 는 한손으로는 무리가 가기 때문에 이를 위해 아래와 같이 작성하여, 한손으로 사용하고자 하였다.

import keyboard

def main():
    
    keyboard.add_hotkey('cmd+z', lambda: keyboard.press_and_release('ctrl+cmd+left'))

    keyboard.add_hotkey('cmd+c', lambda: keyboard.press_and_release('ctrl+cmd+right'))
 
    keyboard.wait()  

if __name__== '__main__':
    main()

 

2. 문제

2.1 Keyboard Package 한/영키 문제

사용 중에 가끔 먹통이 되는 이유가 있었는 데, 원인을 파악해 보니 한/영키 문제 였다.

 

2.2 해결

한/영키를 누를 경우 Keyboard에 hangeul 이라는 것이 늘 따라 붙게 된다. 따라서 앞에 hangeul이라는 것을 붙이면 해결된다. 아래는 해결한 코드이다.

import keyboard

def main():
    
    keyboard.add_hotkey('cmd+z', lambda: keyboard.press_and_release('ctrl+cmd+left'))
    keyboard.add_hotkey('hangeul+cmd+z', lambda: keyboard.press_and_release('ctrl+cmd+left')) # hangeul 붙임

    keyboard.add_hotkey('cmd+c', lambda: keyboard.press_and_release('ctrl+cmd+right'))
    keyboard.add_hotkey('hangeul+cmd+c', lambda: keyboard.press_and_release('ctrl+cmd+right')) # hangeul 붙임

    keyboard.wait()  

if __name__== '__main__':
    main()

 

이렇게 한/영키를 통해 먹통이 되는 Keyboad Pacakge를 앞에 hangeul 이라는 단어를 붙여 해결 할 수 있다.

이전 글에 이어서 Kotlin + Spring Boot 2.0 + Webflux + R2DBC(MySQL)사용방법이다.

 

이전글 - Kotlin Spring Boot Webflux (Eclipse)

2020/04/26 - [Develop/Spring Boot] - Kotlin Spring Boot Webflux (Eclipse)
 

Kotlin Spring Boot Webflux (Eclipse)

이클립스에서 Kotlin + Spring Boot 2.0 + Webflux 사용방법이다. DB연동은 다음에 하며 간단한 프로젝트 생성 후 Get, Post, Put, Delete 부터시작한다. 1. https://start.spring.io/ 불러오는 중입니다... 접속..

wky.kr

 

0. DB 테이블 생성

프로젝트에 사용할 MySQL 테이블을 생성한다

create table test{
	id int(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name varchar(50)
}

 

1. 이전 프로젝트 수정

1.1 TestHandler에 getAllTest 메소드를 추가한다.

 

TestHandler.kt

 

1.2 router패키지의 index를 아래와 같이 변경한다.

index.kt

 

1.3 model의 Test.kt 수정

Test.kt 에서 String으로 선언한 id를 Int형으로 수정한다.

Test.kt

 

2. DB 연결(R2DBC)

2.1 Maven 추가

R2DBC Start Maven과 MySQL Maven을 추가한다.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>

<dependency>
	<groupId>dev.miku</groupId>
	<artifactId>r2dbc-mysql</artifactId>
</dependency>

 

2.2 DB 연결

2.2.1 Config패키지와 R2DBC를 연결할 코틀린파일을 생성한다.

com.example.demo.config 패키지, R2DBCConfig.kt 파일생성

 

 

2.2.2 DB Connection 설정

생성한 R2DBCConfig파일에 AbstractR2dbcConfiguration을 상속받고 아래와 같이 작성한다.

host : DB 접속 URL (ip주소를 입력할 땐 앞에 http를 생략해야한다. http를 붙일 경우 connection error 발생)

password : DB 비밀번호

port : DB 포트

database : DB 이름

username : DB 계정

 

R2DBCConfig.kt

 

2.2.3 @EnableR2dbcRepositories

메인 코틀린 파일에 @EnableR2dbcRepositories 어노테이션을 붙여준다.

DemoApplication.kt

 

3. DAO생성 및 Model 수정

3.1 DAO 패키지와 인터페이스를 생성한다.

TestDAO.kt

 

3.2 DAO 설정

TestDAO에 ReactiveCrudRepository를 추가하고 Model을 연결한다.

TestDAO.kt

 

3.3 Model 수정

Test.kt 파일을 수정한다. DB 연결에 필요한 annotation 및 생성자를 추가한다.

아래의 생성자는 id를 통해 DB의 데이터를 삭제하기 위해 생성하였다.

Test.kt

 

4. Handler 수정

4.1 TestDAO 선언

TestHandler.kt에 생성한 TestDAO를 선언한다.

TestHandler.kt

 

4.2 생성한 Handler Method들 수정

4.2.1 소스 수정

TestHandler.kt

 

 

4.2.2 PostMan으로 테스트

추가

 

전체 조회 :

 

수정 : id 2의 name을 test222로 수정

 

하나만 조회 : 수정한 id : 2 인 데이터가 수정이 잘되었는지 조회

 

삭제 :

 

5. 기타

현재 테스트한 테이블에서는 id가 Auto Increment로 되어있다. 따라서 save 메소드 사용할 경우 insert와 update가 잘 되지만,

만약 auto increment가 아닌 테이블에 데이터를 삽입하게 된다면, save를 사용할 경우 insert가 되지않고 update문이 적용될 것이다.

이를 해결하기 위해선 Persistable이라는 interface를 상속받아 isNew의 반환값을 true로 하면 해결이되나, 이는 또 다시 insert만되고 update는 안되는 반대되는 상황이 발생한다. 

아직 ReactiveCrudRepository에서는 정확한 해결방법이 나오진 않은 것 같다.

 

다음 - R2DBC Join, Query사용 Dynamic Projection

1. Maven Archetype

Archetype이란 Maven(Spring, Spring boot)에서 나만의 template/boilerplate를 제작하는 방법이다.

 

2. 기본세팅

2.1 Setting.xml

- Archetype 사용하기 위해선 setting.xml 파일이 필요하다.

- setting.xml 파일이 없다면, 내문서의 .m2 폴더에 setting.xml 파일을 생성한다.

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">


  <localRepository>${user.home}/.m2/repository</localRepository>

</settings>

 

2.2 Maven

https://maven.apache.org/download.cgi

 

Maven – Download Apache Maven

Downloading Apache Maven 3.6.3 Apache Maven 3.6.3 is the latest release and recommended version for all users. The currently selected download mirror is http://us.mirrors.quenda.co/apache/. If you encounter a problem with this mirror, please select another

maven.apache.org

- Archetype은 기본적으로 Maven명령어를 사용하기 때문에 Maven이 설치되어 있어야 한다.

- 위 사이트에 접속하여 .zip 파일을 다운받아 적절한 위치에 압축을 해제한 뒤 고급시스템 설정의 path에

압축 해제한 경로에 맞게(Ex : C:\apache-maven-3.6.3\bin) path를 추가한다.

 

※ Spring/Spring Boot는 Maven 설치안하는데 Maven이 되는데, 그 이유는 Spring/Spring Boot의 경우 프로젝트를 생성하게 되면, 자동으로 mvnw.cmd 라는 파일이 생기기 때문이다.

 

3. Archetype 사용방법

3.1 미리 생성한 프로젝트에서 생성 후 로컬배포

- Spring/Sprin Boot에서 나만의 template/boilerplate를 제작하였다면, IDE Terminal에서 프로젝트 Root 경로에 아래의 명령어를 입력한다

mvn archetype:create-from-project

 

- 명령어를 입력하게되면 target/generated-sources/archetype 폴더가 생성된다. 생성한 폴더에 접근한다.

cd target/generated-soruces/archetype

 

- IDE Terminal에서 mvn install 또는 mvn deploy를 실행한다.

mvn install

  - mvn install은 local 저장소에 배포를 하며, .m2 폴더에 저장된다.

  - mvn deploy는 maven 저장소에 배포하는 것이며, 사설 nexus 저장소에 배포가능하다.

 

 

- mvn archetype:generate -DarchetypeCatalog=local 명령어를 통해 정상적으로 등록되었는지 확인한다.

mvn archetype:generate -DarchetypeCatalog=local

 

- mvn install 했으나 로컬에 배포되지 않은 경우 mvn archetype:update-locate-catalog 를 입력한다.

mvn archetype:update-locate-catalog

 

3.2 사설 저장소에 배포

- mvn deploy로 사설 nexus에 등록할 경우 target/generated-soruces/archetype 에 있는 pom.xml에 아래와 같이 등록한다. 

	<distributionManagement>
        <repository>
            <id>test-releases</id> 
            <name>Releases Repository</name>
            <url>http://127.0.0.1/repository/maven-releases/</url>
        </repository>
        <snapshotRepository>
            <id>test-snapshots</id>
            <name>Snapshots Repository</name>
            <url>http://127.0.0.1/repository/maven-snapshots/</url> 
        </snapshotRepository>
    </distributionManagement>

 

- 만약 사설 저장소에 권한이 필요해 계정 및 비밀번호가 필요하면, .m2 폴더의 settings.xml에 계정과 비밀번호를 등록한다. server에 들어가는 id 와 위 xml의 id가 같아야한다.

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">


  <localRepository>${user.home}/.m2/repository</localRepository>

  <servers>
    <server>
      <id>test-releases</id>
      <username>admin</username>
      <password>testpw</password>
    </server>
     <server>
      <id>test-snapshots</id>
      <username>admin</username>
      <password>testpw</password>
    </server>

  </servers>
 
</settings>

 

- 등록 후 target/generated-soruces/archetype 경로에서 mvn deploy를 하면 사설저장소에 저장된다.

- 사설 저장소에 있는 것을 사용 및 확인하고 싶다면 setting.xml에 mirror를 작성해줘야 한다.

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">


  <localRepository>${user.home}/.m2/repository</localRepository>

 <mirrors>
	<mirror>
	<id>test-snapshots-repo</id>
	<mirrorOf>*</mirrorOf>
	<name>Mirror.</name>
	<url>http://127.0.0.1/repository/maven-snapshots/</url>
	</mirror>
</mirrors>

  <servers>
    <server>
      <id>test-releases</id>
      <username>admin</username>
      <password>testpw</password>
    </server>
     <server>
      <id>test-snapshots</id>
      <username>admin</username>
      <password>testpw</password>
    </server>

  </servers>
 
</settings>

 

- 등록 후 mvn archetype:generate 하면 등록한 archetype 항목이 나올것이다.

 

4. 프로젝트 생성

- mvn archetype:generate 명령어 후 플젝 선택시 입력하는 것들은

target/generated-soruces/archetype 폴더의 pom.xml에 있는

- groupid

- artifactId

- version

이며, 똑같이 입력해주면 프로젝트가 생성된다.

 

이클립스에서 Kotlin + Spring Boot 2.0 + Webflux 사용방법이다.

DB연동은 다음에 하며 간단한 프로젝트 생성 후 Get, Post, Put, Delete 부터시작한다.

 

R2DBC MySQL 연결 -

2020/06/04 - [Develop/Spring Boot] - Kotiln Spring Boot Webflux r2dbc(MySQL)

 

Kotiln Spring Boot Webflux r2dbc(MySQL)

이전 글에 이어서 Kotlin + Spring Boot 2.0 + Webflux + R2DBC(MySQL)사용방법이다. 이전글 - Kotlin Spring Boot Webflux (Eclipse) 2020/04/26 - [Develop/Spring Boot] - Kotlin Spring Boot Webflux (Eclipse..

wky.kr

 

1.

https://start.spring.io/

접속하여 간단히 프로젝트 부터 생성한다. Dependencies는 Webflux를 위해 Reactive Web만 추가한다.

 

GENERATE를 눌러 프로젝트를 다운받은 후 이클립스에 추가한다.

 

2. 실행하기 전에 Intellij 에서는 실행을 안해봐서 잘 모르겠지만, 이클립스에서 실행할 경우 final 에러가 뜨는데 그 경우

Spring Boot를 실행하는 메인 클래스에 open이라는 키워드를 붙여준다.

 

3.다음 간단히 프로젝트 구조를 잡아준다.

Model에는 Spring이나 Webflux가 아닌 SpringBoot 에서의 VO를 작성해준다.

 

4. Handler를 작성해준다. Handler는 Spring이나 Webflux가 아닌 SpringBoot 에서의 Controller라고 생각하면된다.

 

5. router를 작성해준다. router에서 URL을 설정해 Handler로 연결 시켜준다.

Configuration Annotation을 사용하는 경우 class 앞에 꼭 open 을 붙여주어야 하며,

open이 붙은 클래스들의 함수들 또한 앞에 open을 꼭 붙여주어야 한다.

 

 

6. Postman으로 간단히 실행해본다.

GET : Handler에서 작성한대로 query parameter를 넣어 그 결과를 반환한다.

 

POST : Handler에서 작성한대로 id, name을 전송하게 되면 id, name을 출력한다.

 

PUT : Handler에서 작성한대로 id, name을 전송하게 되면 id, name을 출력한다.

 

DELETE : Handler에서 작성한대로 id, name을 전송하게 되면 id, name을 출력한다.

 

다음에 시간나면 데이터베이스 연결을 위해 ReactiveCrudRepository사용법과 Join하는 방법을 올려야겠다.

 

Kotlin Webflux R2DBC MySQL - 

2020/06/04 - [Develop/Spring Boot] - Kotiln Spring Boot Webflux r2dbc(MySQL)

 

Kotiln Spring Boot Webflux r2dbc(MySQL)

이전 글에 이어서 Kotlin + Spring Boot 2.0 + Webflux + R2DBC(MySQL)사용방법이다. 이전글 - Kotlin Spring Boot Webflux (Eclipse) 2020/04/26 - [Develop/Spring Boot] - Kotlin Spring Boot Webflux (Eclipse..

wky.kr

 

www.blog.wky.kr 

불러오는 중입니다...

https://blog.wky.kr 

불러오는 중입니다...

https://www.blog.wky.kr 

불러오는 중입니다...

blog.wky.kr

'일상' 카테고리의 다른 글

blog.wky.kr  (0) 2020.03.01
블로그 시작.  (1) 2018.10.23

1. 이클립스 삭제 후 재설치 시 발생하는 에러

 - ERROR: org.eclipse.equinox.p2.engine code=4 An error occurred during the org.eclipse.equinox.internal.p2.engine.phases.CheckTrust phase.

 

내 문서의 .eclipse, .m2, .p2 폴더 삭제

 

2. 프로젝트 선택 시 발생하는 에러

- please choose another workspace as

 

해당 프로젝트의 metadata폴더 삭제

CSS를 사용하기 위한 Selector, Combinators(선택자, 결합자(space, >, +, ~))이다.

 

1. Class 선택자.

1-1. 기본 중 하나인 Class 선택자이다.

스타일 태그서 .아이디{ } 로 사용한다.

코드 :

<div class="class-selector">
 	CSS Class Selector - { font-size : 30px; }
</div>

<style>
  .class-selector{
              font-size : 30px;
  }
</style>

 

결과 : 

 

1-2. 특정 태그에만 사용하게 하는 Class 선택자이다. 

특정 태그.클래스명을 적으면 사용가능하다.

아래 코드와 같이 같은 클래스를 사용해도 style에서 태그를 지정해주면 다르게 적용된다.

<div class="class-selector-2">
	CSS Class Selector - { font-size : 30px; color : red; } Not Apply
</div>
<p class="class-selector-2">
	CSS Class Selector - { font-size : 30px; color : red; }
</p>

<style>
p.class-selector-2{
            font-size: 30px;
            color : red;
}
</style>

 

결과 : 

 

2. ID 선택자

기본 중 하나인 ID 선택자이다. 앞에 #을 붙여 사용가능하다.

코드 :

<div id="id-selector">
	CSS ID Selector - { background-color : black; color: white;}
</div>

<style>
        #id-selector{
            background-color : black;
            color : white;
        }
</style>

결과 : 

 

3. Group 선택자 ( , )

element에 같은 Style을 적용하기 위한 선택자이다. 사용할 element들에 콤마로 나열하여 사용가능하다.

코드 :

<div class="comma-1">
	CSS Comma 1 - { color : red; }
</div>

<div class="comma-2">
	CSS Comma 2 - { color : red; }
</div>
        
<style>
        .comma-1,
        .comma-2{
            color : red;
        }
</style>

결과 :

 

4. 결합자(space, >, + ~)

4-1. 자손 선택자 Descendant ( space )

CSS에서 Space로 표현하는 것으로 특정 element 내부에 있는 특정한 모든 element에 적용된다.

아래와 같이 특정 element(descedant-1) 내부에 있는 특정한 모든 element(descedant-2)에 적용된다.

또한 descedant-1 내부의 nothing이라는 class 내부에 있어도 descedant-2 가 적용된다.

코드 : 

        <div class="descendant-1">
            <div class="descendant-2">
                    CSS Descendant - { color : yellow; }
            </div>
            <div class="nothing">
                <div class="descendant-2">
                        CSS Descendant -{ color : yellow; }
                 </div>   
            </div>

            <div class="descendant-2">
                    CSS Descendant - { color : yellow; }
            </div>
        </div>
        
<style>
        .descendant-1 .descendant-2{
            background-color : yellow;
        }
</style>

결과 : 

 

4-2. 자식 선택자 Child ( > )

CSS에서 > (부등호)로 표현하는 것으로 자손 선택자와 마찬가지로 특정 element 내부에 있는 특정한 모든 element에 적용된다.

아래와 같이 특정 element(descedant-1) 내부에 있는 특정한 모든 element(descedant-2)에 적용된다.

하지만 자손선택자와 다른점은 descedant-1 내부의 nothing이라는 class 내부에 있으면 descedant-2 가 적용되지 않고 무조건 descedant-1 의 바로 내부에만 있어야 적용된다.

코드 :

<div class="child-1">
    <div class="child-2">
    	CSS Child - { color : green; }
	</div>
    <div calss="nothing">
        <div class="child-2">
            CSS Child - { color : green; } Not Apply
        </div>   
    </div>

	<div class="child-2">
  		CSS Child - { color : green; }
  	</div>
</div>

<style>
        .child-1 > .child-2{
            background : green;
        }
</style>

결과 : 

 

4-3. 인접 형제 선택자 Adjacent Sibling ( + )

CSS에서 +로 표현하는 것으로 특정한 element 바로 다음에 오는 특정한 element 하나만적용되는 것이다.

아래의 코드처럼 adjacent-1 내부에 adjacent-2 가 있어도 적용되지 않고 항상 특정 element가 닫힌 뒤 바로 다음에 와야 적용이된다. 

또한, 하나만 적용되기 때문에 아래 코드의 마지막 줄은 적용되지 않았다.

코드 :

        <div class="adjacent-1">
            CSS Adjacent - { color : grey; } Not Apply
            <div class="adjacent-2">
                CSS Adjacent - { color : grey; } Not Apply
            </div>
        </div>
        <div class="adjacent-2">
            CSS Adjacent - { color : grey; }
        </div>
        <div class="adjacent-2">
            CSS Adjacent - { color : grey; } Not Apply
        </div>
        
<style>
        .adjacent-1 + .adjacent-2{
            background-color : grey;
        }
</style>

결과 : 

 

4-4. 일반 형제 선택자 General Sibling ( ~ )

CSS에서 ~로 표현하는 것으로 특정한 element 바로 다음에 오는 모든 특정한 element들이 적용된다.

아래의 코드처럼 general-1 내부에 general-2 가 있어도 적용되지 않고 항상 특정 element가 닫힌 뒤 바로 다음에 와야 적용이된다. 

인접 형제 선택자와 달리 하나만 적용되는 것이 아니라 모두 적용되어 아래코드의 마지막 줄도 적용이 되어있다.

코드 :

        <div class="general-1">
            CSS General - { color : blue; } Not Apply
            <div class="general-2">
                    CSS General - { color : blue; } Not Apply
            </div>
        </div>

        <div class="general-2">
            CSS General - { color : blue; }
        </div>

        <div class="general-2">
            CSS General - { color : blue; }
        </div>

        <div class="nothing">
            <div class="general-2">
                CSS General - { color : blue; } Not Apply
            </div>
        </div>

        <div class="general-2">
                CSS General - { color : blue; }
        </div>
        
<style>
        .general-1 ~ .general-2{
            background-color : blue;
        }
</style>

결과 : 

 

사용된 모든 예제 소스 및 결과화면

<html>  
    <head>
        <title>
            CSS TEST
        </title>
    </head>
    <body>
        <div class="class-selector">
            CSS Class Selector - { font-size : 30px; }
        </div>
        <hr/>

        <div class="class-selector-2">
                CSS Class Selector - { font-size : 30px; color : red; } Not Apply
        </div>
        <p class="class-selector-2">
                    CSS Class Selector - { font-size : 30px; color : red; }
        </p>
        <hr/>

        <div id="id-selector">
                CSS ID Selector - { background-color : black; color: white;}
        </div>
        <hr/>

        <div class="comma-1">
            CSS Comma 1 - { color : red; }
        </div>

        <div class="comma-2">
            CSS Comma 2 - { color : red; }
        </div>
        <hr/>

        <div class="descendant-1">
            <div class="descendant-2">
                    CSS Descendant - { color : yellow; }
            </div>
            <div calss="nothing">
                <div class="descendant-2">
                        CSS Descendant -{ color : yellow; }
                 </div>   
            </div>

            <div class="descendant-2">
                    CSS Descendant - { color : yellow; }
            </div>
        </div>
        <hr/>

        <div class="child-1">
            <div class="child-2">
                    CSS Child - { color : green; }
            </div>
            <div calss="nothing">
                <div class="child-2">
                        CSS Child - { color : green; } Not Apply
                    </div>   
            </div>

            <div class="child-2">
                    CSS Child - { color : green; }
            </div>
        </div>
        <hr/>

        <div class="adjacent-1">
            CSS Adjacent - { color : grey; } Not Apply
            <div class="adjacent-2">
                CSS Adjacent - { color : grey; } Not Apply
            </div>
        </div>
        <div class="adjacent-2">
            CSS Adjacent - { color : grey; }
        </div>
        <div class="adjacent-2">
            CSS Adjacent - { color : grey; } Not Apply
        </div>
        <hr/>

        <div class="general-1">
            CSS General - { color : blue; } Not Apply
            <div class="general-2">
                    CSS General - { color : blue; } Not Apply
            </div>
        </div>

        <div class="general-2">
            CSS General - { color : blue; }
        </div>

        <div class="general-2">
            CSS General - { color : blue; }
        </div>

        <div class="nothing">
            <div class="general-2">
                CSS General - { color : blue; } Not Apply
            </div>
        </div>

        <div class="general-2">
                CSS General - { color : blue; }
        </div>
    </body>

    <style>
        .class-selector{
            font-size : 30px;
        }
        p.class-selector-2{
            font-size: 30px;
            color : red;
        }
        #id-selector{
            background-color : black;
            color : white;
        }
        
        .comma-1,
        .comma-2{
            color : red;
        }

        .descendant-1 .descendant-2{
            background-color : yellow;
        }

        .child-1 > .child-2{
            background : green;
        }

        .adjacent-1 + .adjacent-2{
            background-color : grey;
        }

        .general-1 ~ .general-2{
            background-color : blue;
        }
    </style>
</html>

 

1. 먼저 필요한 Maven을 설치합니다.

아래의 메이븐저장소에 들어가서 spring-security-core, spring-security-web, spring-security-config, pring-security-taglibs 검색하여 pom.xml에 추가합니다.

https://mvnrepository.com/

 

Maven Repository: Search/Browse/Explore

Reactivewizard Binding Last Release on Oct 31, 2019

mvnrepository.com

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-taglibs</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>

 

2. web.xml 에 들어가 아래와 같이 추가합니다.

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>/WEB-INF/spring/root-context.xml
		/WEB-INF/spring/security-context.xml
	</param-value>
</context-param>

<filter>
	<filter-name>springSecurityFilterChain</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy
	</filter-class>
</filter>
    
<filter-mapping>
	<filter-name>springSecurityFilterChain</filter-name>
	<url-pattern>/*</url-pattern>

</filter-mapping>

 

3. 로그인 창을 하나 만듭니다.

파일이름은 login.jsp로 만들었습니다.

<!-- login.jsp -->
<form action="loginProcess" method="post">
<input type="text" name="userId">
<input type="password" name="userPw">
<input type="submit" value="ok">
</form>

 

4. /WEB-INF/spring 폴더 밑에 security-context.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"

	xmlns:security="http://www.springframework.org/schema/security"

	xmlns:context="http://www.springframework.org/schema/context"

	xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd

        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">


	<security:http auto-config='true' use-expressions="true">
		<security:csrf disabled="true" />
        
		<security:intercept-url pattern="/login"
			access="permitAll" />

		<security:intercept-url pattern="/**"
			access="isAuthenticated()" />

		<security:form-login login-page="/login"
			username-parameter="userId" password-parameter="userPw"
			login-processing-url="/loginProcess" default-target-url="/test"
			authentication-failure-url="/login?failure"
			always-use-default-target='true' />
            
		<security:logout invalidate-session="true"
			logout-url="/logout" logout-success-url="/login?logout" />
	</security:http>


</beans>

대충 설명하자면 /login 주소는 권한이없어도 접속을 허용하겠다는 뜻이고

그 외 주소는 로그인 인증이 되어야 접속할 수 있도록 하겠다는 뜻입니다.

그 외를 알고싶으시면 use-expressions(SpEL) 을 찾아보시면 됩니다.

그리고 3번에서 만든 로그인페이지를 등록하고 3번에서 만든 각각 id, passwordname들을 지정합니다.

또한 login-proccessing-url에 3번에서 만든 action 의 주소를 입력합니다. 여기서는 loginProcess로 되어있지만 아무거나 해도됩니다.

login-proccessing-url은 로그인 처리를 해주는 url인데 이름만 정해주면 security 측에서 알아서 만들어주는 url으로써  이름만 정하면 따로 저희가 할 일은 없이 spring security가 로그인처리를 진행 해줍니다.

default-target-url은 로그인성공시 접속할 주소를 입력합니다.

auauthentication-failure-url 은 로그인 실패시 처리할 주소를 입력하시면 됩니다.
always-use-default-target='true' 로그인 성공후 default-target-url에 설정한 곳으로 갈지 선택하는 것입니다.

invalidate-session="true" logout-url="/logout" logout-success-url="/login?logout" 로그아웃 입니다. 로그아웃 경로를 저장하고 로그아웃 성공시 돌아갈 경로를 저장해주면 로그아웃이 됩니다. 이 로그아웃url 또한 security 측에서 알아서 만들어주는 url으로써 여기서 이름만 정하면 따로 저희가 만들일 없이 spring security가 알아서 로그아웃을 진행해 줍니다.

 

5. 그리고 위의 security-context.xml 안에 로그아웃 밑에 바로 아래와 같이 추가합니다.

	<security:authentication-manager>

		<security:authentication-provider

			user-service-ref="loginService">

			<security:password-encoder

				ref="bcryptPasswordEncoder" />

		</security:authentication-provider>

	</security:authentication-manager>


	<bean id="loginService" class="net.test.test.service.testService" />

	<bean id="bcryptPasswordEncoder"

		class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />

로그인 진행시 아이디를 가지고 비밀번호를 비교할 서비스클래스를 정하는 것입니다.

또한 데이터베이스에 비빌번호가 bcrypt로 저장되어 있을 경우 비밀번호 비교시 bcrypt로 비밀번호를 비교하기 위해 설정해줍니다.

 

6. 위에서 아이디를 가지고 비밀번호를 비교할 서비스를 생성합니다.

여기서 DB베이스에서 회원정보를 불러와 암호화되어있는 비밀번호를 비교하게됩니다.

public class testService implements UserDetailsService {

	@Autowired
	TestDAO testDAO;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// TODO Auto-generated method stub
		loginVO userVO = testDAO.selectLogin(username);

		if (userVO == null) {
			throw new UsernameNotFoundException("No user found with id");

		}

		Collection<SimpleGrantedAuthority> roles = new ArrayList<SimpleGrantedAuthority>();

		roles.add(new SimpleGrantedAuthority("ROLE_USER"));

		UserDetails user = new User(userVO.getId(), userVO.getPassword(), roles);
		return user;

	}

}

UserDetailsService라는 것을 implements합니다.

그리고 username을 통해 데이터베이스에서 회원정보를 가져오고

roles에 유저를 선택한 다음 UserDetails를 생성하여 return 만 해주면 Spring Security가 알아서 비밀번호를 데이터베이스에 있는 것과 form에서 넘어온 비밀번호를 비교하여 맞으면 로그인성공해주고 아니면 로그인실패를 해줍니다.(그냥 조회만 했을 뿐인데 알아서 다 해줌)

 

이렇게 bean설정과 UserDetails만 설정하면 로그인form에서 아이디하고 비밀번호를 전달한 뒤 아이디만 조회했을 뿐인데 spring security가 로그인처리까지 다 진행해줍니다.

다음 controller에서 Authentication을 통해 로그인한 정보(아이디)를 가져와 사용할 수 있습니다.

1. 먼저 필요한 Maven을 설치합니다.

아래의 메이븐저장소에 들어가서 

mybatis, mybatis-spring, spring-jdbc, mariadb, hikaricp를 검색하여 maven을 pom.xml dependency에 추가합니다.

https://mvnrepository.com/

 

Maven Repository: Search/Browse/Explore

AWS Java SDK :: Regions Last Release on Oct 18, 2019

mvnrepository.com

예시)

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.5.2</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>2.0.1</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>4.3.4.RELEASE</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
		<dependency>
			<groupId>org.mariadb.jdbc</groupId>
			<artifactId>mariadb-java-client</artifactId>
			<version>2.5.0</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP -->
		<dependency>
			<groupId>com.zaxxer</groupId>
			<artifactId>HikariCP</artifactId>
			<version>3.4.0</version>
		</dependency>

 

2.  webapp/WEB-INF/spring/ 에 있는 root-context.xml에 들어갑니다.

Namespaces에 들어가여 아래와 같이 체크해주고 저장합니다.

 

3. 다시 root-context에서 Source에 들어가 아래와 같이 입력해줍니다.

	<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
		<property name="driverClassName"
			value="org.mariadb.jdbc.Driver"></property>
		<property name="jdbcUrl"
			value="jdbc:mariadb://127.0.0.1:3306/spring?useSSL=false&amp;serverTimezone=Asia/Seoul"></property>
		<property name="username" value="DB계정입력"></property>
		<property name="password" value="DB비밀번호입력"></property>
	</bean>

	<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"
		destroy-method="close">
		<constructor-arg ref="hikariConfig" />
	</bean> 

	<bean id="sqlSessionFactory"
		class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<property name="mapperLocations"
			value="classpath:mappers/**/*.xml"></property>
	</bean>

	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">

		<property name="basePackage" value="net.test.test.dao" />
	</bean>

간단한 설명을하자면 먼저 아래의 xml 코드는 DB커넥션풀을 hikariCP를 사용하고 DB에 연결하겠다는 뜻입니다.

	<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
		<property name="driverClassName"
			value="org.mariadb.jdbc.Driver"></property>
		<property name="jdbcUrl"
			value="jdbc:mariadb://127.0.0.1:3306/spring?useSSL=false&amp;serverTimezone=Asia/Seoul"></property>
		<property name="username" value="DB계정입력"></property>
		<property name="password" value="DB비밀번호입력"></property>
	</bean>

	<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"
		destroy-method="close">
		<constructor-arg ref="hikariConfig" />
	</bean> 

아래의 xml코드는 sqlSession을 연결할 수 있게하며, 데이터베이스 쿼리를 갖고있는 mapper들을 연결시켜주겠다는 의미입니다.(아래에 작성되어있음.)

<bean id="sqlSessionFactory"
		class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<property name="mapperLocations"
			value="classpath:mappers/**/*.xml"></property>
	</bean>

아래는 mapper에 작성되어있는 쿼리들을 불러오는 dao들을 스캔하는 의미입니다. value는 꼭 패키지에 맞춰 쓸 수 있도록 합니다.

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">

		<property name="basePackage" value="net.test.test.dao" />
	</bean>

 

4. 아래와 같이 패키지와 controller 및 dao를 작성합니다.

임의로 작성한것입니다. 꼭 이렇게 안하셔도됩니다.

DAO파일은 인터페이스로 생성해야합니다.

 

5. 이제 쿼리를 작성할 mapper를 생성합니다. 

아래의 resources/ 폴더 밑에 mappers와 그 아래 xml 파일을 생성합니다.

xml파일에 아래와 같이입력합니다.

mapper의 namespace는 아까 작성한 TestDAO의 경로와 똑같이 해야합니다.

여기서 DB쿼리들을 작성합니다. DB접속이 잘 되었는지 확인만 하기위해 현재 시간을 나타내주는 쿼리를 작성해봅니다.

6. TestDAO를 열어 아래와 같이 TestMapper.xml에서 작성한 selectNow함수를 작성해줍니다.

그리고는 작성한 controller 에 들어갑니다.(현재 글에는 TestController입니다.)

아래와 같이 작성합니다.

MVC패턴을 이용하는 Spring에서는 아래와같이 controller에서 불러오면 안되는 것은 아니지만 패턴의 규칙이 있기때문에 controller에서 직접 불러오는 것을 추천드리지는 않지만, 테스트목적이라 여기서는 불러서 사용합니다.

 

이것을 실행하여 localhost:8080/test에 접속하면 db에서 불러온 현재 시간이 console창에  나타나는 것을 볼 수 있습니다.

1. 먼저 아래에 접속해 STS 3.9.X버전을 설치한다.

https://spring.io/tools3/sts/all

 

Spring Tool Suite™ 3 (STS 3) Download page

Use one of the links below to download an all-in-one distribution for your platform. Choose either a native installer or simple archive, they contain equivalent functionality

spring.io

그리고 최신버전 또는 옛날

버전을 설치한다.

2. 프로젝트 생성에서 MVC PROJECT를 설치한다.

3. 프로젝트의 pom.xml에 들어가 밑줄친 부분을 1.8버전, 4.3.x버전으로 변경한다

아래와 같이 변경

4. maven-compiler-plugin을 1.8버전으로 변경한다

아래와 같이 변경한다

 

전부 변경하였다면 저장해준다.

5. 프로젝트 우클릭 후 Properties를 클릭한다.

Project Facets에서 1.6으로 되어있는 Java버전을 1.8로 변경한다

Dynamic Web Module 또한 3.0으로 변경하여도 상관없으나 만약 변경한다면 프로젝트의 web.xml파일에 들어가서

web.xml 파일 상단에 web-app version="2.5" 버전을 web-app version="3.0"으로 같이 변경해준다.

6. Java Compiler 에 접속하여 버전을 1.8버전으로 변경해준다

만약 변경되어 있지 않거나 없다면 아래 사진의 파란색부분인 Java Build Path을 클릭한다.

Java Build Path에서 Add Library를 클릭해준다.

JRE System Library를 선택한다.

Installed JREs를 클릭한다.

Add를 클릭한다.

Standard VM을 선택한다.

Directory를 클릭하여 Java 1.8버전이 있는 폴더를 선택하여 Apply버튼을 눌러주어 1.8버전을 선택하고 다시 6번의 Proejct Facets에 들어가 1.8로 수정해주면 된다.

7. 프로젝트 우클릭 후 Update Project를 해준 뒤 실행해보면 잘 실행이된다.

 

참고로 pom.xml에 있는 servlet-api 버전이나, jsp, jstl 버전을 최신버전으로 변경해주어도 좋다

+ Recent posts