'스프링 부트' 태그의 글 목록 :: 잡다한 프로그래밍
반응형

1. Transaction에서 rollback이 되지않는 경우는?

- checked exception은 트랜잭션에서 롤백되지않는다. (명시적인 예외처리가 필요한 것, try catch해야하는것 들)

- unchecked exception은 롤백대상 (예외처리를 하지 않아도 IDE에서 에러 뱉지 않음, Runtime Exception...)

 

2. checked exception 경우에도 rollback을 하고싶으면?

만약 B에서 에러가 발생했을때 A를 rollback시키고 싶다면? 이럴때 transaction을 이용한다

하지만 B의 exception이 checked exception이라면?

@Transactional
public void test() {
    A();
    B();
}

만약 아래처럼 try catch를 이용하면 에러를 catch에서 처리했기 때문에, rollback이 발생하지 않는다.

@Transactional
public void test() {
    A();
    try {
    	B(); // checked exception을 발생시키는 부분
    } catch {
    
    }
}

 

- @Transcational 어노테이션에 rollbackFor라는 옵션을 이용한다

해당 에러는 throw하고, rollbackFor옵션을 사용하면 checked exception도 확인할 수 있게 된다.

@Transactional(rollbackFor = {Exception.class})
public void test() {
    A();
    B(); // checked exception을 발생시키는 부분에서는 error throw
}

 

- checked 예외를 unchecked 예외로 변경하여 throw한다

public void B() {
    try {
    
    }catch(Exception e) {
    	throw new RuntimeException("예외");
    }
}


@Transactional
public void test() {
    A();
    B(); // unchecked exception으로 바꿈
}

 

3. 아래와 같은 경우에는 롤백이 일어날까 일어나지 않을까?

	// ATest 클래스
    @Transactional
    public void A() {
        try {
            test.B();
        } catch (RuntimeException e) {
            System.out.println("예외 처리");
        }
    }
    
    
    // BTest클래스
    @Transactional
    public void B() {
        //로직
        mapper.save();
        throw new RuntimeException("예외"); // unchecked exception
    }

 

해당 코드는 다음과 같이 동작한다.

1. A클래스의 트랜잭션이 실행된다.

2. B메소드가 실행되면서 1번의 트랜잭션에 참여한다 (기본 propagation 속성이 PROPAGATION_REQUIRED)

3. save를 실행하는 부분의 처리가 끝나고 트랜잭션의 완료처리 (completion)을 진행함

4. checked Exception이 일어나면서 트랜잭션이 완료처리 됨

5. checked Exception때문에 해당 트랜잭션을 롤백 규칙을 적용(기본 규칙적용), 해당 메소드에서 바로 롤백하지않고 rollback mark를 함 (해당 마크는 전역으로 관리함)

6. A의 로직 수행 > 1번에서 생성된 트랜잭션의 완료처리가 진행, 이때 rollback mark를 확인해서 값이 true라면 rollback진행

따라서 이경우에는 롤백되어버린다

 

해당 propagation 옵션을 변경할경우 해당 rollback mark 문제를 해결할 수 있다

 

PROPAGATION_REQUIRES_NEW 사용시 매번 새로운 트랜잭션 생성

- A와 B가 각각 트랜잭션을 생성하고, 매 번 commit하므로 서로의 rollback에 영향을 미치지 않음

하지만 오버헤드 발생할 수 있음 (매번 새로운 커넥션 생성)

반응형
반응형

1. 스프링 시큐리티란?

  • authentication(인증) 과 authorization(권한 부여)를 제어하는 프레임워크
  • De-facto (정해진 표준은 아니지만 사실상 표준)
  • servlet filter를 기반으로함(리퀘스트를 가로채서 선처리하고 리스폰스를 가로채서 후처리함) ex)filter로 UTF-8 인코딩 가능
  • 필터로 등록된 DelegatingFilterProxy가 어떤 URL을 가로챌것인지 정한다


2. 스프링 시큐리티 실습하기 (커스텀 로그인)

 

1) pom.xml에 spring-security dependency 추가하기

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

 

2) web.xml에 filter추가

	<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) security-context.xml 작성

/WEB-INF/spring/appServlet 경로에 security-context.xml을 작성한다. 먼저 name은 text, password는 test1234로 ROLE_USER를 만들고 /secured의 경로로 접근할경우 ROLE_USER인 사람만 접근할 수 있게 설정한다

<?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"
	xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<security:authentication-manager>
		<security:authentication-provider>
			<security:user-service>
            	<security:user name="test" authorities="ROLE_USER" password="test1234"/>
            </security:user-service>
        </security:authentication-provider>
	</security:authentication-manager>


	<security:http use-expressions="true" auto-config="true">
		<security:intercept-url pattern="/secured/**"
			access="hasRole('ROLE_USER')" />
            
		<security:form-login login-page="/login" />
		<security:logout logout-url="/logout" />
	</security:http>
</beans>

 

4) web.xml 수정

다음과 같이 context-param내부에 security-context.xml경로를 추가한다

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

5) LoginController 작성

@Controller
public class LoginController {

  @RequestMapping(value=“/login”, method = RequestMethod.GET))
  public String login() {

  	return "login";
}

 

5) login.jsp 작성

다음과 같은 login.jsp를 작성한다 csrf 토큰값을 함께 사용하여 csfr공격에 대비한다

<form action="<c:url value="/login" />" method='POST'>

  <table>
    <tr><td>User:</td><td><input type='text' name='username' value=''></td>   </tr>
    <tr><td>Password:</td><td><input type='password' name='password' /></td>  </tr>
    <tr><td colspan='2'><input name="submit" type="submit“ value="Login" /></td> </tr>

    <input type="hidden"  name="${_csrf.parameterName}"value="${_csrf.token}"/>
  </table>

</form>

 

6) 결과

secured로 요청을 보내면 custom login form으로 요청이 전송되고 이를 지정된 ROLE_USER가 맞는지 판단함

 

7) 추가정보

로그인 실패 : /login?error로 요청이 전달됨

로그아웃 성공 : /login?logout으로 요청이 전달됨

따라서 다음과 같이 처리가능

@RequestMapping(value="/login", method = RequestMethod.GET)
public String login(
    @RequestParam(value = "error", required = false) String error, 
    @RequestParam(value="logout", required=false) String logout, 
    Model model) {

    if (error != null) {
        model.addAttribute("errorMsg", "Invalid username and password");
    }

    if(logout != null) {
         model.addAttribute("logoutMsg", "You have been logged out successfully ");
     }

     return "login";

}

 

세션의 시간 설정 및 중복로그인 방지 방법 invalid-session-url = 세션 시간초과

max-sessions = 최대 로그인하는 수, expired-url 중복로그인시 보낼 url

	<security:session-management invalid-session-url="/login?timeout=1">
			<security:concurrency-control max-sessions="1" expired-url="/login" />
		</security:session-management>
반응형
반응형

지난 #8 강의에 이어 MYSQL과 연동하여 Update, delete, insert 하는 방법을 설명하도록 한다.

 

1. INSERT

먼저 지난번 query와 달리 update메소드를 사용하며 다음과 같이 사용한다 update메서드 내부에는 insert sql문, new Object로 name, email, text를? 에 넣어주는 방식으로 사용하며 이때 반환하는 값이 1이면 true를 리턴한다.

	public boolean insert(Offer offer) {
		String sqlStatement = "insert into offers (name, email, text) values (?,?,?)";
		String name = offer.getName();
		String email = offer.getEmail();
		String text = offer.getText();
		
		return (jdbcTemplateObject.update(sqlStatement, new Object[]{name, email, text}) == 1);
	}

2. UPDATE

insert와 같이 update메소드를 사용하며 사용방법은 다음과 같다.

	public boolean update(Offer offer) {
		String sqlStatement = "update offers set name=?, email=?, text=? where id=?";
		String name = offer.getName();
		String email = offer.getEmail();
		String text = offer.getText();
		int id = offer.getId();
		
		return (jdbcTemplateObject.update(sqlStatement, new Object[]{name, email, text, id}) == 1);
	}

3. DELETE

마찬가지로 update 메소드를 사용하며 사용방법은 다음과 같다.

	public boolean delete(int id) {
		String sqlStatement = "delete from offers where id = ?";
		
		return (jdbcTemplateObject.update(sqlStatement, new Object[]{id}) == 1);
	}
반응형
반응형

1. DI(Dependency Injection)란?

DI는 말 그대로 의존성을 주입한다 라는 뜻이다. 쉽게 설명하면 JAVA에서 객체를 new를 이용하여 생성하는데 이렇게 사용하지 않고 외부에서 생성한 객체를 세터 또는 생성자를 통해 사용하겠다는 의미이다. 이를 의존성 주입이라 한다. 의존성 주입 방법에는 세터를 이용하거나 생성자를 이용한 방법 두가지가 있다. 예제로는 생성자를 이용한 방법으로 예제를 공부하겠다

DI 사용하지 않은 예시

그림처럼 DI를 이용하지 않고 new를 사용하여 객체를 생성하게 되면 PetOwner와 Dog간 강한 결합이 생기게 되어 도그를 수정하면 PetOwner가 수정될 수 있다.

 

DI

반면 다음과 같이 Bean Container를 이용하여 객체를 생성하고 의존성 주입을 시켜주면 결합이 강하지 않다는 장점이 생긴다. 이때 Bean Container는 XML로 이루어져 있다.

DI 설명

다음 그림처럼 메인함수에서는 ApplicationContext라는 BeanContainer를 생성하고 미리 작성된 xml에 따라 ApplicationContext는 Dog, Cat, PetOwner라는 객체를 생성하고 Dog, Cat을 PetOwner에 의존성 주입한다. 스프링은 new방식 대신 DI방식을 권장한다.


2. 의존성 주입 실습

#1 프로젝트 생성

File > New > Spring Legacy Project를 클릭하고 다음과 같은 Simple Spring Maven 프로젝트를 생성한다. 프로젝트명은 원하는 프로젝트명을 사용해도 무방하다. 이 실습에서는 helloDI라는 프로젝트명을 사용하겠다.

프로젝트 생성

#2 AnimalType, Cat, Dog 생성하기

src/main/java에 클래스와 인터페이스를 담을 패키지를 생성한다 패키지명은 자유로워도 좋다. 패키지를 생성하였다면 패키지 아래에 AnimalType.java라는 인터페이스를 생성한다

인터페이스 코드는 다음과 같다 인터페이스를 implements할 cat, dog클래스들이 사용할 sound라는 메소드를 정의하고 있다.

 

AnimalType.java

package kr.ac.hansung.helloDI;

public interface AnimalType {

	public void sound();

}

 

다음으로 Cat.java라는 클래스를 생성한다. 게터와 세터가 만들어져 있으며 AnimalType의 sound메소드를 사용하고 있다.

 

Cat.java

package kr.ac.hansung.helloDI;

public class Cat implements AnimalType {

	public String getMyName() {
		return myName;
	}

	public void setMyName(String myName) {
		this.myName = myName;
	}

	private String myName;

	@Override
	public void sound() {
		
		System.out.println("Cat name = " + myName + ":" + "Meow");
	}

}

 

마지막으로 Dog.java라는 클래스를 생성한다. 게터와 세터가 만들어져 있으며 AnimalType의 sound메소드를 사용하고 있다.

 

Dog.java

package kr.ac.hansung.helloDI;

public class Dog implements AnimalType {

	public String getMyName() {
		return myName;
	}

	public void setMyName(String myName) {
		this.myName = myName;
	}

	private String myName;

	@Override
	public void sound() {
		
		System.out.println("Dog name = " + myName + ":" + "Bow Wow");
	}

}

 

#3 PetOwner클래스 생성하기

petOwner클래스는 AnimalType의 animal을 받는다 이는 cat이나 dog를 받아서 사용하겠다는 의미이고 play라는 메소드와 생성자를 정의했다.

 

PetOWner.java

package kr.ac.hansung.helloDI;

public class PetOwner {

		private AnimalType animal;

		public PetOwner(AnimalType animal) {
			this.animal = animal;
		}
		
		public void play() {
			
			animal.sound();
		
		}
}

 

#4 animal.xml 생성하기

Bean Container가 생성하기 위해 참고할 xml파일을 생성한다 코드는 다음가 같다.

<bean id = "dog"> 부분이 dog라는 클래스의 객체를, <bean id = "cat"> 부분이 cat이라는 클래스의 객체를 <bean id = petOwner"> 부분이 petOwner의 객체를 생성하고 <constructor-arg ref = "dog">는 dog객체를 의존성 주입하겠다 라는 의미이며, <property name="myName" value="poddle">는 도그의 myName의 값을 poodle로 주겠다는 의미이다.

 

animal.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 http://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="dog" class="kr.ac.hansung.helloDI.Dog">
		<property name="myName" value="poodle"></property>
	</bean>

	<bean id="cat" class="kr.ac.hansung.helloDI.Cat">
		<property name="myName" value="bella"></property>
	</bean>

	<bean id="petOwner" class="kr.ac.hansung.helloDI.PetOwner">
		<constructor-arg ref="dog"></constructor-arg>
	</bean>
</beans>

 

#5 MainApp.java작성

마지막으로 메인 함수인 MainApp.java를 다음과 같이 작성한다. ApplicationContext가 animal.xml을 바라보게 하고

petOwner의 person이 context의 getBean함수를 통해 객체를 가져와 사용하는 모습이다.

package kr.ac.hansung.helloDI;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {

	public static void main(String[] args) {

		ClassPathXmlApplicationContext context = 
				new ClassPathXmlApplicationContext("/kr/ac/hansung/helloDI/conf/animal.xml");
		
		PetOwner person = (PetOwner) context.getBean("petOwner");
		person.play();
		
		context.close();
	}

}

 

#5 프로젝트 Run

프로젝트를 Run 하게 되면 다음과 같은 결과를 확인할 수 있다.

결과화면

 

사용자는 animal.xml에서 정의한 대로 dog, cat, petOwner의 객체를 먼저 생성하고, 이후 dog를 petOwner에 주입하였으니 결과 화면에는 dog가 보이게 된다 만약 cat을 주입하고 싶다면 constructor-arg의 ref = cat으로 수정해주면 된다

반응형
반응형

1. 정적(Static) 페이지란?

정적 페이지

다음 사진과 같이 사용자의 request가 있을 때 (index.html을 요청하는 것) 웹 서버는 파일 시스템의 index.html을 사용자에게 response 해주는 방식이 정적 페이지 방식이다.


2. 동적(Dynamic) 페이지란?

동적 페이지

위 사진과 같이 사용자의 request를 웹 애플리케이션 서버(톰캣)에서 Servlet에 전달한다

Servlet내부에서는 요청에 맞는 재정의 된 함수(doGet)를 실행하고 얻은 결과를 response 해준다

이때 DB와 함께 연동된 처리일 수 있으며 사용자는 함수에 따라 동적인 결과를 얻게 된다.


3. 웹 애플리케이션 서버 구성도

웹 어플리케이션 서버 설명

위 사진과 같이 WAS가 구성된다 앞서 설명한 동적 페이지와 유사한 그림이다

사용자의 request를 web server가 jsp, servlet으로 전달하고 이를 동적으로 처리하고 (이때 DB와의 커넥션이 있을 수 있음) 이를 다시 Web Server로 다시 client에게 전달하는 방식으로 처리하게 된다


4. 구현하기

#1. 프로젝트 생성

프로젝트 생성

File > New > Dynamic Web Project를 선택하여 프로젝트를 생성한다.

프로젝트 생성

Project name을 설정한다 여기서는 servlettest로 설정하였다.

Target runtime은 톰켓 8.0 버전으로 진행하였다.

프로젝트 생성

설정해 줄 것이 없으므로 다음으로 넘어간다

프로젝트 생성

Web.xml을 생성하기 위해 체크를 해주고 Finish를 누른다

 

#2. index.html 생성

html 만들기

생성된 프로젝트에 WebContent를 우클릭하여 다음과 같이 html 파일을 생성한다.

html 만들기

index.html의 코드는 다음과 같다

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Servlet Test Page</title>
</head>
<body>
	<form action="DoLogin" method="POST">
		Username: <input type="text" name="username"> <br/>
		Password: <input type="password" name="password"> <br/>
		<input type="submit" value="제출"/>
	</form>
</body>
</html>

기본적인 흐름을 알기 위한 코드로 form 안에 username을 입력하기 위한 input과 password를 입력하기 위한 input이 존재한다 form은 POST방식으로 전달하기 위해 method = POST이며, action은 실제 서블릿에서 DoLogin이라는 함수에서 처리한다는 의미이다.

 

#3. index.html 결과 화면

html 결과

만약 html을 성공적으로 작성하고 실행하였다면 다음과 같은 결과가 확인된다.

이때 사용자가 제출 버튼을 누르게 된다면 다음과 같은 오류 화면이 발생할 것이다.

 

error 화면

왜냐하면 /DoLogin을 처리해줄 서블릿이 존재하지 않기 때문이다 따라서 /DoLogin을 처리해줄 서블릿을 생성해보자.

 

#4. 서블릿 생성 하기

서블릿 패키지 생성

생성된 프로젝트에 Src를 우클릭하여 다음과 같이 Package를 생성한다. 패키지명은 원하는 명으로 해도 상관없으나 이 프로젝트에서는 servlet이라고 패키지명을 작성하였다.

서블릿 생성

생성된 패키지를 우클릭하여 New > Servlet을 클릭하여 서블릿을 생성한다

서블릿 생성

서블릿 명은 DoLogin으로 작성 하였다 (이는 html form에서 action을 DoLogin으로 지정했기 때문)

서블릿 생성

다음은 자동으로 URL이 /DoLogin으로 매핑되어있으므로 건드리지 않고 넘어간다.

서블릿 생성

다음은 재정의 하여 사용할 함수의 목록을 보여준다 아래에 현재 doPostdoGet이 체크되어있지만 실습에서는 POST를 사용하기로 하였으므로 doPost만 체크하고 서블릿 생성을 완료한다.

 

생성된 DoLogin 서블릿의 코드는 다음과 같다.

package servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class DoLogin
 */
@WebServlet("/DoLogin") //URL에노테이션으로 되어 있음
public class DoLogin extends HttpServlet { //HttpServlet상속 받아야함
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public DoLogin() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		PrintWriter out = response.getWriter();
		String htmlResponse = "<html>";
		htmlResponse += "<h2> Your name is : " + username + "<br/>";
		htmlResponse += "<h2> Your password is : " + password + "<br/>";
		htmlResponse += "</html>";
		
		out.println(htmlResponse);
	}
}

String username = request.getParameter("username")으로 form의 username input이 보낸 value를 저장하고

PrintWriter를 사용해 html을 response해준다.

 

@WebServlet("/DoLogin")이 부분이 form에서 /DoLogin으로 요청을 보낸 부분을 처리하겠다고 명시해주는 에노테이션 부분이다. 에노테이션에 대한 자세한 설명은 ~~에서 확인할 수 있다.

 

#5. 프로젝트 실행 결과

다음과 같이 서블릿을 생성을 완료하고 이전과 같이 html을 제출하였다면 다음과 같은 화면이 보이게 될 것이다.

프로젝트 성공

5. 설명

코드 구조

프로젝트에서 작성한 구성을 그림으로 표현하면 다음과 같다 사용자는 html폼을 이용하여 POST방식으로 /DoLogin이라는 요청을 보내고 서블릿에서는 DoLogin 클래스의 재정의된 doPost를 사용하여 이를 처리하여 사용자에게 다시 반환하는 방식이다.

 

간단한 서블릿을 활용한 웹 서버 애플리 케이션 예제였다

반응형

+ Recent posts