이클립스에서 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

 

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을 통해 로그인한 정보(아이디)를 가져와 사용할 수 있습니다.

'Develop > Spring' 카테고리의 다른 글

Spring Security URL login  (0) 2021.01.22
Spring Tomcat JNDI 설정  (0) 2020.11.17
Spring Security Custom Filter  (0) 2020.11.09
Spring security 로그인(DB에 있는 아이디 조회)  (0) 2019.11.02
Spring Mybatis + MariaDB(HikariCP) 설정  (0) 2019.10.24
Spring 3 에서 4로 migration  (0) 2019.10.22

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창에  나타나는 것을 볼 수 있습니다.

'Develop > Spring' 카테고리의 다른 글

Spring Security URL login  (0) 2021.01.22
Spring Tomcat JNDI 설정  (0) 2020.11.17
Spring Security Custom Filter  (0) 2020.11.09
Spring security 로그인(DB에 있는 아이디 조회)  (0) 2019.11.02
Spring Mybatis + MariaDB(HikariCP) 설정  (0) 2019.10.24
Spring 3 에서 4로 migration  (0) 2019.10.22

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 버전을 최신버전으로 변경해주어도 좋다

'Develop > Spring' 카테고리의 다른 글

Spring Security URL login  (0) 2021.01.22
Spring Tomcat JNDI 설정  (0) 2020.11.17
Spring Security Custom Filter  (0) 2020.11.09
Spring security 로그인(DB에 있는 아이디 조회)  (0) 2019.11.02
Spring Mybatis + MariaDB(HikariCP) 설정  (0) 2019.10.24
Spring 3 에서 4로 migration  (0) 2019.10.22

Python Slack :

2018/11/03 - [Develop/Python] - Python Slack WebHook (파이썬 슬랙 웹훅) 만들기

2018/11/03 - [Develop/Python] - Python Slack Bot (파이썬 슬랙 봇) - slackclient

2018/11/03 - [Develop/Python] - Python Slack Bot (파이썬 슬랙 봇) - slacker

2018/11/08 - [Develop/Python] - Python Slack Lunch Bot - 점심 알리미 봇

 

SpringBoot Slack :

2018/11/24 - [Develop/Spring Boot] - Spring boot Slack WebHook. 슬랙 웹훅(Java Slack WebHook)

 

 

Node.js 에서 슬랙 웹훅을 사용하는 방법이다.

1. 먼저 아래의 링크에서 슬랙에서 웹훅을 추가하는 방법을 보고온다.

2018/11/03 - [Develop/Python] - Python Slack WebHook (파이썬 슬랙 웹훅) 만들기

 

Python Slack WebHook (파이썬 슬랙 웹훅) 만들기

파이썬으로 슬랙에 메세지를 보낼 수 있는 방법 중 하나인 웹훅(webhook)을 사용하는 방법이다. - Spring boot Webhook (Java Webhook) 2018/11/24 - [Develop/Spring Boot] - Spring boot Slack WebHook. 슬랙 웹..

pangyeon.tistory.com

2. https://api.slack.com/community 슬랙 api 사이트에서 Node.js에서  WebHooks을 사용할 수 있는 api를 찾아본다.

 

이 글에서 사용하는 api는 CoffeScript에 있는 slack-node-sdk 이다.

 

3. slack-node-sdk 에 이미 사용방법들을 상세히 설명되어 있다.

 

npm install slack-node --save

를 입력하여 모듈을 설치한다.

 

다음 .js파일을 생성하거나 이미 사용하고 있는 프로젝트에 아래와 같은 코드를 입력하고 실행한다.

 

//slack.js
var Slack = require('slack-node');

webhookUri = "webhookUri를 적는란";

slack = new Slack();
slack.setWebhook(webhookUri);

slack.webhook({
    channel: "#general",	// 현 슬랙의 채널
    username: "웹훅", // 슬랙에서 보여질 웹훅 이름
    text: "테스트"	//텍스트
}, function (err, response) {
    console.log(response);
});

node slack.js 으로 실행하면 아래와 같이 메세지가 슬랙에 전송된다.

 

 

참고 : https://api.slack.com/community

 

Slack Platform: Community | Slack

Slack developer tools Widely-used open source libraries With hundreds of open source libraries, plugins, and apps covering more than a dozen languages and frameworks, it's tough to know where to begin. Here are some popular libraries supporting a wide rang

api.slack.com

  1. 지구방위대 다시보기 2020.07.10 11:18

    잘 보고 갑니다~~

윈도우 환경에선 아래의 주소에서 .zip 또는 .msi 파일을 다운받아 설치 가능하다. 참고로 64비트만 지원한다고 한다.

https://github.com/MicrosoftArchive/redis/releases

 

MicrosoftArchive/redis

Redis is an in-memory database that persists on disk. The data model is key-value, but many different kind of values are supported: Strings, Lists, Sets, Sorted Sets, Hashes - MicrosoftArchive/redis

github.com

.msi 로 설치할 경우 

C:\Program Files\Redis 이 경로에 설치가 되며

redis-sever.exe 로 서버 실행이 가능하며 , redis-cli.exe로 클라이언트 실행이 가능하다.

.msi 로 설치 하면 바로 redis 서버가 실행이 되므로 본인이 확인하고 싶다면

관리자 권한 cmd로 들어가

net stop redis 명령어를 입력해 redis를 멈춘 뒤 redis-server.exe로 다 시실행하면 된다.

 

아주 간단한 명령어만 살펴본다.

>SET keyname "hello"
OK
>GET keyname
"hello"

set 을 통해 key와 value를 입력할 수 있고

get 을 통해 key의 value를 조회할 수 있다.

 

>TTL keyname
(integer) -1

TTL을 통해 key의 만료시간을 알 수 있다.

-1은 기한이 없는 것을 말하고 밀리초 단위로 반환한다.

 

>DEL keyname
(integer) 1

DEL 명령어를 통해 key를 삭제할 수 있고 삭제한 키의 개수를 반환한다.

 

>SET keyname "hello"
OK
>SET keyname2 "hello2"
> keys *
1) "keyname"
2) "keyname2"

keys pattern

*는 모든 키들 검색

keys 명렁어를 통해 모든 key들 또는 검색한 key들을 알 수 있으나, key가 많아지면 성능이 저하될 수 있으므로 현재 배포 중인 서비스에서는 사용하지 않는다.

(scan이라는 명령어를 사용함)

 

 

https://www.acmicpc.net/problem/12865

 

12865번: 평범한 배낭

첫 줄에 물품의 수 N(1 ≤ N ≤ 100)과 준서가 버틸 수 있는 무게 K(1 ≤ K ≤ 100,000)가 주어진다. 두 번째 줄부터 N개의 줄에 거쳐 각 물건의 무게 W(1 ≤ W ≤ 100,000)와 해당 물건의 가치 V(0 ≤ V ≤ 1,000)가 주어진다. 입력으로 주어지는 모든 수는 정수이다.

www.acmicpc.net

 

Knapsack 알고리즘을 사용하면 풀리는 문제이다.

최근에 알고리즘 문제 풀이에 취미가 생겨 문제를 열심히 푸는 중인데, Knapsack 알고리즘을 봐도 이해가 안되어

나름 내 방식대로 이해하고 풀다 보니 Knapsack알고리즘과 똑같이 나왔다 ㄷㄷ.

 

주어진 개수 = n

주어진 무게 = weight

주어진 무게의 가치 = value

Knapsack 알고리즘은 전체 무게가 weight(주어진 무게)를 넘지 않도록 n 번째까지 항목 중에서 얻어진 최고 이익을

저장하여 Dynamic Programing으로 푸는 방식이다.

처음에 이게 무슨말인지 이해가 안되어 이해를 포기하고 그냥 내 생각대로 해보니 Knapsack알고리즘이 나왔는 데,

이 글 또한 그러한 사람들을 위해 작성 또는 내가 다시보려고 작성한 글이다.

 

문제에 나와있는 무게와 가치

6 13

4 8

3 6

5 12

말고

아래의 가치로 계산해보고 n(주어진 개수)=5 , WEIGHT(주어진 무게의 값)=15 라고 하자

weight value
1 1
2 2
3 3
4 4
5 5

 

5*15 로 2차원 배열을 잡는다. 실제로 생성할 땐 dp[n+1][value+1] 로 생성하자 dp[0][0~15]을 써야하기 때문.

또한. 값을 [1][1] 부터 집어넣자.

 

첫 번째 항목인

weight=1과 value=1을 먼저 계산한다.

 

weight=1과 주어진 무게의값 WEIGHT(15)까지 계산을 한다

계산은 각 열의 무게와 weight=1을 빼서 0보다 클경우 value를 집어 넣고

만약 작을 경우 그 이전의 값(바로 위의 행)을 집어넣는다. 그 이전의 값이란 것은 두번째 또는 세번째 항목부터 이해가 될 것이다.

 

현재 weight=1이고 첫 번째 열의 무게는 1이다 1-1>=0 이므로 1을 넣는다.

현재 weight=1이고 두 번째 열의 무게는 2이다 2-1>=0 이므로 1을 넣는다.

이런식으로 첫 번째 항목을 채운다

 

만약 첫 번째 항목의 weight=5라면 첫 번째 열에서는 1-5 >=0 이 아니므로 0을 넣는 데, 사실 0이라기보단 그 이전의 값(바로 위의 행)인 dp[0][1]값을 넣는 것이다. 두번째 또는 세번째 항목부터 이해가 될 것이다.

(행은 아이템 순번, 열은 무게)

두 번째 항목은 weight=2이고 value=2이다

 

현재 weight=2이고 첫 번째 열의 무게는 1이다 1-2>=0 이 아니다. 따라서 이 전의 값(바로 위의 행)을 넣어야한다.

이전의 값(바로 위의 행)은 dp[1][1]값인데 이는 즉, dp[n-1][j] 정도로 볼 수 있다.

 

현재 weight=2이고 두 번째 열의 무게는 2이다 2-2=>0 이므로 첫 번째 항목처럼 2를 넣는다라고 생각할 수 있지만

아니라 이전(바로 위의 행)의 값과 값을 비교해야한다.

이전(바로 위의 행)의 값은 1이고 현재 나의 값은 2이다 2가 더 크므로 2를넣는다.

 

현재 weight=2이고 세 번째 열의 무게는 3이다 3-2>=0 이고 3-2=1이다. 이제 남은 1의 값을 활용해야 한다.

이전(바로 위의 행)의 열을 사용한다. 

3-2=1 이기 때문에 이전 값(바로 위의 행)의 열은 1,1 -> dp[1][1]이다

현재 weight는 2이며, value=2이고 현재 열은 세 번째 열이다. 2, 3 -> dp[2][3]

현재 열은 이전 값(바로 위의 행)의 1번열의 값 + 현재 값 -> dp[2][3] = dp[1][1]+value -> dp[2][3]=1+2=3

dp[2][3]=3이란 것을 알 수 있다.

현재 weight=2일 때 세 번째 열의 무게가 3이라는 것을 알게 되었으면 이제 또 이전의 값(바로 위의 행)과 비교를 해야한다. 바로 위의행 dp[1][3]=1 이고 현재 dp[2][3]은 3이라는 것을 알고 3이 더크므로 3을 완전히 집어 넣는다.

 

이로써 규칙은

1. 먼저 그 열의 무게와 현재 항목의 무게를 빼고 무게가 0보다 큰지 확인한다. 

2. 0보다 작다면 이전의 값(바로 위의 행)값을 넣는다.

3. 0보다 크다면 현재 열의 무게와 현재 항목의 무게를 뺀 값을 더 한다.

더하는데 빼서 나온 값은 그 이전의 값(바로 위의 행에서 뺀 값의 열번째) 와 현재 항목의 가치를 더한다.

4. 값을 더했다면 이전의 값(바로 위의 행)의 값과 비교를 해 더 큰 값을 완전히 집어 넣는다.

 

이렇게 해서 두번째도 쭉 집어넣는다.

 

세 번째 항목은 weight=3이고 value=3이다

 

현재 weight=3이고 첫 번째 열의 무게는 1이다 1-3>=0 이 아니다. 따라서 이 전의 값(바로 위의 행)을 넣어야한다.

이전의 값(바로 위의 행)은 dp[2][1]값이다. 따라서 1을 넣는다

 

현재 weight=3이고 두 번째 열의 무게는 2이다 2-3>=0 이 아니다. 따라서 이 전의 값(바로 위의 행)을 넣어야한다.

이전의 값(바로 위의 행)은 dp[2][2]값이다. 따라서 2을 넣는다

 

현재 weight=3이고 세 번째 열의 무게는 3이다 3-3>=0 이다. 따라서 규칙대로 행동한다.

규칙의 3번인 0보다 크기 때문에 현재 열의 무게와 현재 항목의 무게를 뺀값을 더하는 데 3-3=0이다.

그리고 이 0의 값은 그 이전의 값( 바로 위의 행에서 뺀값의 열번째)는 dp[2][0]인데 이는 0이다.(dp[n+1][value+1] 로 잡았고 값을 [1][1]부터 잡았기때문에 0번쨰는 0으로 초기화 해준다.)

어쨋든 dp[2][0] + 현재 항목의 가치 -> dp[2][0]+3 -> 0+3 =3

그리고 규칙의 4번으로 와서 3과 그 이전의 값(바로 위의 행) dp[2][3]의 값과 비교해서 큰 값을 넣어준다. 둘이 값이 3으로 같기 때문에 3을 넣어준다.

 

현재 weight=4이고 네 번째 열의 무게는 4이다 4-3>=0 이다. 따라서 규칙대로 행동한다

규칙의 3번인 0보다 크기 때문에 현재 열의 무게와 현재 항목의 무게를 뺀값을 더하는 데 4-3=1이다.

그리고 이 1의 값은 그 이전의 값( 바로 위의 행에서 뺀값의 열번째)는 dp[2][1]인데 이는 1이다.

어쨋든 dp[2][1] + 현재 항목의 가치 -> dp[2][1]+3 -> 1+3 =4

그리고 규칙의 4번으로 와서 4와 그 이전의 값(바로 위의 행) dp[2][4]의 값과 비교해서 큰 값을 넣어준다. 

dp[2][4]=3이고 현재는 4이기 때문에 4가 더크므로 4를 넣어준다.

 

이런식으로 하다보면 아래의 값들이 나온다

 

 

이게 Knapsack알고리즘인데 이를 알고리즘적으로 풀이하면

dp[i][j] = Math.max(dp[i - 1][j], value[i] + dp[i - 1][j - weight[i]]);

이렇게 된다.

 

BOJ 12865 평범한 배낭 문제의 최종 소스는 아래와 같다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st;
		String T;

		T = br.readLine();
		st = new StringTokenizer(T);

		int N = Integer.parseInt(st.nextToken());
		int K = Integer.parseInt(st.nextToken());

		int weight[] = new int[N + 1];
		int value[] = new int[K + 1];
		int dp[][] = new int[N + 1][K + 1];
		for (int i = 1; i < N + 1; i++) {
			T = br.readLine();
			st = new StringTokenizer(T);
			weight[i] = Integer.parseInt(st.nextToken());
			value[i] = Integer.parseInt(st.nextToken());
		}

		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= K; j++) {
				if (j - weight[i] >= 0) {
					dp[i][j] = Math.max(dp[i - 1][j], value[i] + dp[i - 1][j - weight[i]]);

				} else {
					dp[i][j] = dp[i - 1][j];
				}
			}
		}
		System.out.println(dp[N][K]);
	}
}


Node.js에서 google oauth 인증하는 방법이다. 

이전에는 passport를 사용하지 않는 방법을 이용하여 JWT와 함께 쿠키로 로그인 인증처리를 했다면,

이번엔 세션을 이용하여 passport를 이용하는 방법이다.


기본적으로 https://console.cloud.google.com 에서 프로젝트를 생성했다는 가정하에 작성한다.


1. passport를 이용하지 않은 google oauth 인증 후 jwt 사용

2019/01/07 - [Develop/Node.js] - [Node.js] google oauth 인증 (구글 로그인)




1. 적당한 프로젝트를 생성 후 아래와 같은 명령어를 입력한다.


npm init
npm install express --save
npm install express-session --save
npm install session-file-store --save
npm install passport --save
npm install passport-google-oauth


express 를 사용할 것이기 때문에 express를 설치한다. 그리고 passport는 기본적으로 session을 이용하기 때문에 express-session도 설치하고

session 저장소를 파일로 처리할 것이기 때문에 session-file-store 를 설치한다.

그리고 사용할 passport와 passport의 전략중 하나인 passport-google-oauth를 설치한다.


※아래엔 express-session에서 파일로 세션을 저장하는 것 말고 데이터베이스 등을 사용하는 방법을 확인 할 수 있다.

https://www.npmjs.com/package/express-session

2. express를 설정하고 다음 session을 설정한다.


먼저 server.js 파일을 생성하고 아래와 같이 작성한다.


https://www.npmjs.com/package/express-session 에 접속해서 기본적인express-session을 사용하기 위해

아래 주석 //1의 미들웨어를 설정한다.

주석 //2번은 localhost:3000으로 접속할 때 나오는 기본 화면이다.

주석 //3은 express를 통해  3000포트를 이용하여 서버를오픈한다.

var express = require('express');
var session = require('express-session')
var FileStore = require('session-file-store')(session)
var app = express();

app.use(session({                // 1
    secret: 'keyboardcat',
    resave: false, 
    saveUninitialized: true,
    store: new FileStore()
}))

app.get('/', function (req, res, next) {    //2
    console.log('홈')
    
})


app.listen(3000, function () {            //3
    console.log('Example app listening on port 3000!');
});


3.  Google Cloud Platform 에서 Json 파일을 다운받고, config 폴더를 만든 뒤 다운받은 json 파일을 config폴더에 넣는다.


사진 맨 오른쪽 하단에 빨간줄로 표시한 아이콘을 클릭하여 json파일을 다운받는다.


다운받은 json 파일엔 기본적으로 아래와같이 나와있다.



이 파일을 아래 처럼 넣는다. 이유는 그냥 가져다 쓰는 것보다 저렇게 가져오는 것이 조금이나마 안전하기 때문.



4. passport를 설정한다.


4-1. passport 전략 중 하나인 우리가 사용할 passport-google-oauth를 사용한다.


server.js에 추가한다.

//1 아래와 같이 passport를 가져오고

//2 passport를 초기화시킨다.

//3 session을 이용하기 때문에 추가한다.

//4 실질적으로 로그인할 수 있는 경로와 scope이다. scope에서는 email이나 기타 정보를 가져 올 수도 있다.

//5 메인페이지에 4번에서 설명한 실질적으로 로그인하는 경로를 설정한다.

var passport = require('passport'),
      GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;    //1

app.use(passport.initialize());    //2
app.use(passport.session());        //3

app.get('/auth/google',            //4
  passport.authenticate('google', { 
      scope: 'https://www.googleapis.com/auth/plus.login' }))

app.get('/', function (req, res, next)   {    //5
  
    var html = `
    login with google`
    res.send(html)
})


위와 같이 설정하고 'node server.js' 명령어를 통해 실행해서 접속하면 아래와 같은 화면이 뜬다.



4-2. 구글 로그인을 해서 정보를 얻어올 로직과 세션에 담을 로직을 구현한다.


//1 은 로그인했을 시 얻어오는 정보가아니라 그냥 임의로 넣었다 사실 이렇게 하는 것은 아니고 바로 아래에서 다시 설명할 예정

//2 로그인 성공하고 로그인 성공했을 때 가져오는 user.id를 세션에 설정한다.

//3 로그인이 성공했으면 페이지를 항상 새로 불러올 때마다 불러오는 것이다. 

그리고 세션에서 serialize에서 설정한 user id를 불러온 후 user 정보를 보여주는 것이다.

//4 아까 json파일에서 client_id와 secret, redirect uri를 불러온다.

//5 로그인이 성공했을 경우 scope를 설정한대로 구글측에서 profile 정보를 가져온다.


//5를 통해 로그인이 성공하면 scope에서 설정한 대로 profile에서 정보를 가져오는데 

성공적으로 가져왔으면 이를 done을 통해 정보를 설정하면

serializeUser로 가서 설정하고

설정된 값이 deserializeUser에서 불러온다.


다시말하자면 원래 var loginData를 설정하는 것이아니라

//5에서 일단 로그인 인증 처리를 한다 -> 데이터베이스에 넣거나, 데이터베이스에 있는 값을 가져와 회원이 맞는지 비교를 한다든지

이런 기본적인 로그인 처리를 설정하고. 이것이 통과된다면 done을 통해 정보를 return하면

//2의 serializeUser가 호출되는데 여기서 user.id ( 꼭 id가아니라 email이면 user.email도 가능) 을 session에 설정한다.

//3 그럼 페이지가 항상 호출될 때마다 deserializeUser가 호출 되는데 deserializeUser의 id를 통해

데이터베이스에 접속해서 이 아이디가 로그인이 된 상태라든지 이런 것들을 확인할 수 있다.

(너무 이상하게 설명한 것 같은데 이해가 되지않는다면 댓글에 적어주시면 감사하겠습니다.)


var config = require('./config/google.json')


var loginData = {        //1 id : 'test', pw : '1' } passport.serializeUser(function(user, done) {    //2 console.log('serialize', user); done(null, user.id); }); passport.deserializeUser(function(id, done) {    //3 console.log('deserialize', id); done(null, loginData) }); passport.use(new GoogleStrategy({                //4 clientID: config.web.client_id, clientSecret: config.web.client_secret, callbackURL: config.web.redirect_uris[0] }, function(accessToken, refreshToken, profile, done) {    //5 return done(null, loginData) } ));


4-3. 로그인이 정상 처리 되어 callback될 주소를 설정한다.


//1 Json파일의 redircet_uri를 설정한다 기본적으로 /auth/google/callback 이다.

//2 로그인이 정상 처리 된다면 //3의 redirect('/') 홈으로 가고 

실패하면 /bye로 보냈다. 이것은 다시 로그인 하는 곳으로 보낼 수 있고 처리하는 것은 개발자 마음이다.

//4 이것을 설정하지 않으면 세션이 스토어에 저장하는 데 오래걸릴 경우 리다이렉트를 시킬 수 있기 때문에 이를 방지하여

세션이 스토어에 저장이 완료되면 리다이렉트를 시키는 코드이다.

//5 로그인 실패시 보내는 주소이다.

//6 위에서 설정한 app.use(passport.session()) 미들웨어를 통해 req에서 user를 호출 할 수 있다. 

이것을 통해 deserilize가 호출 되는 것을 확인할 수 있다.


app.get('/auth/google/callback',     //1
passport.authenticate('google', {     //2
    failureRedirect: '/bye' 
    }),
    function(req, res) {                //3
        req.session.save(function(){     //4                              
          res.redirect('/');
        })
    });


app.get('/bye', function (req, res, next) {    //5
    res.send('login failed')
})

app.get('/', function (req, res, next) {
    console.log(req.user)        //6
    var html = `
    login with google`
    res.send(html)
})


5. 결과



node server.js 명령어를 통해 실행 후

login with google을 클릭하여 로그인을 하게되면

로그인이 성공하면 serialize를 호출하고 

페이지를 새로고침할 때마다 deserialize가 호출 되는 것을 볼 수 있다.



6. 전체 코드



var express = require('express');
var session = require('express-session')
var FileStore = require('session-file-store')(session)
var config = require('./config/google.json');
var app = express();

app.use(session({
    secret: 'keyboardcat',
    resave: false, 
    saveUninitialized: true,
    store: new FileStore()
}))

var loginData = {
    id : 'test',
    pw : '1'
}
var passport = require('passport'),
      GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;

app.use(passport.initialize());
app.use(passport.session());

passport.serializeUser(function(user, done) {
    console.log('serialize', user);
    done(null, user.id);
  });
  
passport.deserializeUser(function(id, done) {
    console.log('deserialize', id);
    done(null, loginData)
  });


passport.use(new GoogleStrategy({
    clientID: config.web.client_id,
    clientSecret: config.web.client_secret,
    callbackURL: config.web.redirect_uris[0]
  },
  function(accessToken, refreshToken, profile, done) {
        return done(null, loginData)
    }
));

app.get('/auth/google',
  passport.authenticate('google', { 
      scope: 'https://www.googleapis.com/auth/plus.login' }))


app.get('/auth/google/callback', 
passport.authenticate('google', { 
    failureRedirect: '/bye' 
    }),
    function(req, res) {
        req.session.save(function(){    
                                       
            res.redirect('/');
        })
    });

app.get('/', function (req, res, next) {
    console.log(req.user)
    var html = `
    login with google`
    res.send(html)
})

app.get('/bye', function (req, res, next) {
    res.send('login failed')
})

app.listen(3000, function () {
    console.log('Example app listening on port 3000!');
});



참고 : 

https://www.npmjs.com/package/express-session 


+ Recent posts