잡다한 프로그래밍 :: 잡다한 프로그래밍
반응형

1. 예시

만약 DB로부터 정보를 얻어오는 다음과같은 예제를 구현했다고 가정하면, 예제는 정상작동하지만, 메소드의 종류가 늘어나면 Connection 부분이 반복적으로 수행된다. 이는 DB의 정보가 바뀌면 메소드의 개수만큼 수정이 일어나야한다는 단점이 있고, Connection부분이 각 A, B회사가 다르게 구성하고 싶을때 UserDao부분의 수정이 일어나야하므로 적절한 방법으로의 수정이 필요하다.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class UserDao {

	public void insertUser(User user) throws ClassNotFoundException, SQLException {
		Class.forName("com.mysql.jdbc.Driver");
		Connection c = DriverManager.getConnection("jdbc:mysql://localhost/test?serverTimezone=UTC", "root", "1234");
		PreparedStatement ps = c.prepareStatement("INSERT INTO USERS(ID, NAME, PASSWORD) values(?,?,?)");
		ps.setString(1,user.getId());
		ps.setString(2,user.getName());
		ps.setString(3,user.getPassword());

		ps.executeUpdate();
		
		ps.close();
		c.close();
	}
	
	public User getUserList(String id) throws ClassNotFoundException, SQLException {
		Class.forName("com.mysql.jdbc.Driver");
		Connection c = DriverManager.getConnection("jdbc:mysql://localhost/test?serverTimezone=UTC", "root", "1234");
		PreparedStatement ps = c.prepareStatement("SELECT * FROM USERS WHERE id = ?");
		ps.setString(1,id);

		ResultSet rs = ps.executeQuery();
		rs.next();
		User user = new User();
		user.setId(rs.getString("id"));
		user.setName(rs.getString("name"));
		user.setPassword(rs.getString("password"));
		
		rs.close();
		ps.close();
		c.close();
		
		return user;
	}
}

 

따라서 제공하는 다음과 같은 구조를 이용하여 코드를 개선한다. ConnectionMaker라는 인터페이스를 통하여 다음과 같이 수정한다.

UserDao

public class UserDao {
	private ConnectionMaker connectionMaker;
    
    public UserDao(ConnectionMaker connectionMaker) {
		this.connectionMaker = connectionMaker;
    }
    
    public void insert.....
    
    public User get.....
    
}

 

ConnectionMaker interface

public interface ConnectionMaker {
	
    public Connection makeConnection() throws ClassNotFoundException, SQLException;
}

 

AConnectionMaker(A회사의 구현 클래스)

public class AConnectionMaker implements ConnectionMaker {
	...
    
    public Connection makeConnection() throws ClassNotFoundException, SQLException {
    	// A회사의 Connection을 생성하는 코드
    }
}

 

2. IOC란

IOC란 Inversion Of Control의 약자로, 제어의 역전이라는 뜻을 가지고 있다.

일반적으로 프로그램은 자신이 사용할 오브젝트를 직접 선택하고, 생성한다

new를 통해 어떤 오브젝트를 생성할건지 선택해 놓음.

private ConnectionMaker connectionMaker = new AConnectionMkaer();

앞선 예제에는 UserDao에서 new를 통해직접적으로 오브젝트를 생성하는대신, 생성자 파라미터를 통해 오브젝트를 선택하게된다. 즉 변화에 유연한 코드를 만들면서 DAO를 수동적인 상태로 변환시켰다.

또한 필요한 오브젝트를 직접 만들지않고, 이후에 나올 Factory가 만들어 준 오브젝트를 전달받게 된다.

즉 제어의 역전이란, 말 그대로 제어권을 역전 시키는 것으로써, 제 3자(Factory)에게 오브젝트에 대한 제어권을 넘겨주고, 자신은 제 3자가 선택하고 생성한 오브젝트를 받아서 사용하는 수동적인 상태가 되는 것을 말한다. 스프링은 IOC를 기반으로 개발자가 활용할 수 있게 해주는 프레임워크이다.

 

DaoFactory(A, B 회사중 선택할 수 있는 클래스)

public class DaoFactory {
	public UserDao userDao() {
    	ConnectionMaker connectionMaker = new AConnectionMaker(); //A회사 선택
        UserDao userDao = new UserDao(connectionMaker); //DI
        return userDao;
    }
}

다음과 같이 구성하게되면 더이상 UserDao는 수정될 필요없는 부분을 유지하고 수정될 여지가 있는 부분은 인터페이스를 implement한 클래스들이, 어떤 클래스를 UserDao가 사용할지는 Factory부분에서 정해주는것을 확인할 수 있다. 이렇게 interface를 이용한 부분이 IOC를 설명하고 있다.

 

3. DI / 의존성 주입

DI란 Dependency Injection의 약자로, 의존관계 주입 이라는 의미를 가지고 있다.

의존 관계란 하나의 오브젝트에서 다른 오브젝트를 사용할 때를 말한다. A라는 클래스에서 B라는 클래스를 사용할 경우, A클래스는 B클래스에 의존하고 있다 라고 표현한다. 즉 UserDao는 ConnectionMaker Interface에 의존한다. 따라서 A회사인지 B회사인지의 여부는 알지 못하는 상태이다.

 

이때 AConnectionMaker클래스를 생성해서 UserDao클래스에 넣어주는 과정, 이를 의존성 주입이라고 보면 된다.

 

즉 Factory에서 다음과 같은 부분이 DI가 일어나는 부분이다

UserDao userDao = new UserDao(connectionMaker); //DI

 

4. 정리

기존 IOC 개념 = 객체 new로 생성안하고 다른곳에서 생성해서 사용하는것 정도로 알고 있던것이 명확하게 알게되었고, 앞서 나온 Interface를 사용한 패턴이 전략패턴이라는것을 알게 되었다 전략패턴은 IOC에 기본이 되는 패턴이라 한다.

 

또한 스프링에서 Factory부분을 컨테이너(ApplicationContext)로 제공하기 때문에 Factory를 통해 의존성 주입을 하지 않아도 된다.

반응형
반응형

1. 정의

개발하며 발생하는 오류를 아무조취를 취하지 않거나 콘솔에 찍는것은 좋은 방법이 아님. 따라서 모든 예외는 적절하게 복구되던지, 작업을 중단시키고 운영자 또는 개발자에게 분명하게 통보가 되어야함.

 

2. 예외의 종류

Error

java.lang.Error 클래스의 서브 클래스로 시스템에 비정상적인 상황이 발생했을때 사용된다. 주로 VM에서 발생시키는것으로 코드상에서 에러를 찾으려고 하면 안된다.

 

Exception과 체크 예외

java.lang.Exception 

 

java.lang.Exception 클래스와 그 서브클래스로 정의되는 예외는 개발자가 만든 애플리케이션 코드 작업중에 예외상황이 발생하였을 경우에 사용한다. 이 중 체크예외는 RuntimeException 클래스를 상속하지 않은 부분을 의미한다.

 

RuntimeException과 언체크 예외

java.lang.RuntimeException 클래스를 상속한 예외들을 의미하고, 명시적인 예외처리를 강제하지 않기 때문에 언체크 예외라고 불린다. 에러와 마찬가지로 catch나 throws로 잡아주지 않아도 된다. 주로 프로그램의 오류가 있을때 발생한다.

 

3. 예외처리 방법

예외 복구

사용자가 요청한 파일을 읽으려 했으나 파일이 없거나, 읽히지 않아서 IOException이 발생한 경우, 사용자에게 상황을 알려주고 다른 파일을 이용하도록 하는 과정, 이 때 사용자에게 예외상황을 안내만하는경우는 복구라고 할 수 없다.

 

이는 네트워크 오류로인해 sql이 실패할경우 대기시간을 거쳐 여러번 시도하게하는 예시이다.

int maxretry = MAX_RETRY;
while(maxretry --> 0){
	try{
	..
    }
    catch(SomeException e){
    //로그 출력 정해진 시간만큼 대기
    }
    finally {
    //리소스반납 정리작업
    }
}
throw new RetryFailedException();

 

예외처리 회피

throw문으로 선언하여 예외가 발생하면 알아서 던져지게 하거나, catch문으로 예외를 잡고 로그를 남기고 다시 예외를 던지는 방법을 의미한다. 이때 내가 처리하지않고 회피한다는것이 catch로 예외를잡고 예외를 발생하지 않은것처럼 하는것은 아니다.

 

이는 JDBC Template에 에러를 넘기는 예시이다. 하지만 콜백이나 템플릿이 아닌 자신의 코드에서 발생하는 예외를 던지는것은 회피가 아니다.

public void add() throws SQLException {
 //JDBC API
}
public void add() throws SQLException {
	try{
    	//JDBC API
    }
    catch(SQLException e){
    	throw e;
    }
}

 

예외 전환

예외 회피와 비슷하게 예외를 복구해서 정상적인 상태로 만들 수 없기 때문에 예외를 메소드 밖으로 던지는 것 이다. 하지만 예외 회피와 달리 예외를 분명하게 의미있는 예외로 바꾸어 전달하는것을 의미한다.

예를들어 새로운 사용자를 등록하려 했을때 DB에 중복되는 아이디가 있다면 JDBC API는 SQLException을 발생시킨다 이를 DuplicateUserIdException으로 바꾸어 던지는것을 의미한다.

public void add(User user) throws DuplicateUserIdException, SQLException {
	try{
    	//JDBC를 이용해 user 정보를 DB에 추가하는 코드
    }
    catch(SQLException e){
    	//ErrorCode가 Mysql의 "Duplicate Entry(1062)" 이면 예외 전환
        if(e.getErrorCode() == MysqlErrorNumbers.ER_DUP_ENTRY)
        	throw DuplicateUserIdException();
        else
        	throw e;
    }
}

 

반응형
반응형

1. 기존 UserDao의 의존관계 주입

기존의 Factory를 이용하지 않은 방법으로 new를 통해 UserDao에서 다음과 같이 생성했는데 이 방법은 UserDao가 사용할 구체적인 클래스를 알고 있어야한다는 점이문제가 있다. 왜냐면 UserDao는 ConnectionMaker 인터페이스 뿐만 아니라, DConnectionMaker를 사용하겠다는것 까지 관리하고 있는 셈이다.

public UserDao() {
	connectionMaker = new DConnectionMaker();
}

 

따라서 최종적으로 만들어진 방법이 DaoFactory이다. DaoFactory는 런타임 시점에 UserDao가 사용할 ConnectionMaker타입의 오브젝트를 컨테이너 설정을 통해 결정하고 이를 생성하고 UserDao의 생성자 파라미터로 주입하여 UserDao가 DConnectionMaker와 관계를 가지도록 해준다 이러한 부분이 의존성 주입을 의미하고 UserDao는 ConnectionMaker 인터페이스와만 의존관계를 이루게 된다.

 

이러한 의존성 주입을 할 수 있는 방법은 생성자 외에도 여러 방법이 존재한다.

 

2. 메소드를 이용한 의존관계 주입

수정자(setter) 메소드를 이용한 주입

setter를 이용한 UserDao는 다음과 같다.

private ConnectionMaker connectionMaker;

public void setConnectionMaker(ConnectionMaker connectionMaker){
	this.connectionMaker = connectionMaker;
}

 

 

3. XML을 이용한 Configuration

JAVA 어노테이션 대신 XML을 이용하여 다음과 같이 설정할 수 있다.

@Bean
public ConnectionMaker connectionMaker() {
    return new DConnectionMaker();
}
<bean id="connectionMaker" class="spring...DConnectionMaker"/>

 

setter를 사용한 메소드를 <property>를 통해 다음과 같이 관계를 주입할 수 있다.

<bean id="userDao" class="UserDao">
	<property name="connectionMaker" ref="connectionMaker"/>
</bean>

 

 

반응형
반응형

1. 직접 생성한 Factory와 스프링 Factory의 차이점

직접 생성하게된 Factory는 호출할때 마다 오브젝트가 달라지고 스프링 Factory는 호출해도 같은 오브젝트를 리턴한다. 이는 스프링 Factory는 싱글톤 패턴으로 구성되어 있기 때문이다.

 

2. 싱글톤 레지스트리로서의 애플리케이션 컨텍스트

스프링은 기본적으로 별다른 설정을 하지 않으면 내부에서 생성하는 빈 오브젝트를 싱글톤 오브젝트로 생성한다. 애플리케이션 컨텍스트는 이러한 싱글톤 객체를 저장하고 관리하는 레지스트리 역할을 한다. 이때 스프링에서의 싱글톤은 JAVA 싱글톤 디자인패턴과 비슷한개념이지만 구현방법은 다르다.

 

싱글톤을 사용하는 이유는 스프링이 처음 설계 되었을때 서버에서 수백 수천번씩 요청을 받아 처리 할 환경이 필요했는데 이를 요청이 올때마다 오브젝트를 생성한다고 가정하면 오브젝트 생성이 너무 많아지고, 가비지 컬렉션의 성능이 좋아짐에도 서버에 부하가 온다 따라서 싱글톤 패턴을 선택하였다.

 

기존 JAVA 싱글톤 패턴

  1. 기본방법

     사용자가 getInstance를 통해 Single 객체를 사용할 때 객체가 null이면 생성하고 null이 아니면 기존의 것을 가져다 사용하는 방식. 하지만 멀티 스레드 환경에서 두개의 스레드가 null일때 접근하면 하나만 생성되지 않을 수 있음.

public class Single {
	private static Single single;
	
	private Single() {};
	
	public static Single getInstance() {
		if(single == null) {
			single = new Single();
		}
		return single;
	}
}

 

  2. synchronized를 이용한 방법

     이 방법을 이용하면 멀티스레드 환경에 적절하게 대응 할 수 있으나 속도가 너무 느리다는 단점이 있다.

public class Single {
	private static Single single;
	
	private Single() {};
	
	public static synchronized Single getInstance() {
		if(single == null) {
			single = new Single();
		}
		return single;
	}
}

 

   3. volatile 이용하기

다음과 같은 volatile을 이용해 미리 객체를 선언하고 이후에 getInstance를 이용한 방식이다. 만약 Single을 사용하지 않을 경우에도 객체가 생성되어 있다는 단점이 있다.

public class Single {
	private static volatile Single single =  new Single();
	
	private Single() {};
	
	public static synchronized Single getInstance() {
		return single;
	}
}

 

  4. holder 이용하기

다음과 같은 내부 클래스를 이용한 방법으로 싱글톤을 구현할 수 있다. Single이 로드되지 않으면 먼저 생성되어있는 객체도없을 뿐더러, getInstance가 불릴때 Holder클래스가 로드되면서 single객체를 생성하므로 스레드에도 안전하다.

public class Single {

	private Single() {};
	
	private static class Holder {
		private static final Single single = new Single();
	}
	
	public static Single getInstance() {
		return Holder.single;
	}
}

 

기존 JAVA 싱글톤 패턴의 단점

  1. private 생성자를 가지고 있어 상속할 수 없다
  2. 테스트하기에 어렵다 (단위 테스트에 안좋음)
  3. 서버환경에서 싱글톤이 하나만 만들어지는것을 보장하지 못한다(여러 JVM에 분산설치될 경우)
  4. 싱글톤의 사용은 전역 상태를 만들 수 있어 바람직하지 못하다. (public static이므로)

이러한 단점으로 인해 스프링에서는 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공하는데 이를 싱글톤 레지스트리 라고 한다.

싱글톤 레지스트리는 JAVA 싱글톤과 달리 ApplicationContext가 로드될때 IOC/DI를 통해 빈을 생성하고 빈목록에 이를 저장해 놓고 이후에 ApplicationContext를 이용해 빈을 가져다가 사용하는 방식으로 되어있다. 즉 ApplicationContext가 싱글톤 레지스트리로서의 역할을 한다.

 

싱글톤 레지스트리의 장점

private static 이 아닌 평범한 자바 클래스를 싱글톤처럼 활용할 수 있게 한다. 이는 public 생성자를 가질 수 있고 앞서나온 단점이 해결된다.

 

3. 싱글톤과 오브젝트의 상태

이러한 싱글톤은 멀티스레드 환경이라면 여러 스레드가 동시에 접근할 수 있는 문제가 생기므로 상태정보를 가지고 있지 않은 stateless방식으로 만들어져야한다.

만약 다수의 스레드가 싱글톤 오브젝트의 변수를 수정하게 되면 서로 값을 덮어쓰고 자신이 저장한 값이 아닌 값을 읽어올 수 있기 때문에 기본적으로 인스턴스 필드의 값을 변경하고 유지하는 상태유지 방식으로 만들지 않는다. 

 

따라서 DB나 서버의 리소스로부터 생성한 정보는 파라미터, 로컬 변수, 리턴 등으로 해결하면 된다.

 

다음과 같은 문제가 생기는 Dao가 있다 싱글톤으로 빈이 만들어지는데 Connection 변수와, User를 로컬변수가 아닌 private 변수로 선언했다.

이렇게 되면 User와 Connection는 매번 새로운 값으로 값이 바뀌는 상태정보를 가지고있으므로 문제가 생길 수 있으므로 잘못된 방법이다. 하지만 connectionMaker는 처음에만 값이 정해지고 이후는 읽어서 사용하는 읽기정보이므로 사용해도 무방하다.

 

싱글톤에서 문제가 생기는 UserDao

public class UserDao {
    private ConnectionMaker connectionMaker;
    private Connection c;// 문제가 되는 부분
    private User user; //문제가 되는부분
    
    public User get(String id) throws ClassNotFoundException, SQLException {
    	this.c = connectionMaker.makeConnection();
    	...
    }
}

 

반응형
반응형

0. 개요

앞선 Dao를 분리하는 작업을 하면서 생성한 오브젝트를 스프링에서는 빈이라고 부른다. 스프링에서 이러한 Bean을 IoC방식에 따라 생성하고 관계를 설정하는것을 컨테이너(빈 팩토리)가 담당한다. 보통 빈팩토리 대신 application context를 주로 사용한다. application context는 빈 팩토리보다 좀 더 확장된 기능을 가지고 있고 일종의 설정 파일을 읽어 빈을 생성하고 관계를 정의해주는 역할을 담당한다.

 

1. 컨테이너 설정파일

다음과 같이 Java로 설정파일을 작성할 경우 @Configuration이라는 어노테이션에 따라 이 클래스를 컨테이너가 사용할 설정 파일임을 명시한다. 또한 @Bean 어노테이션에 따라 오브젝트 생성을 담당하는 IoC기반의 메소드임을 표시한다.

 @Configuration
 public class DaoFactory
 	@Bean
    public UserDao userDao(){
    	return new UserDao(connectionMaker());
    }
    
    @Bean
    public ConnectionMaker connectionMaker() {
    	return new DConnectionMaker();
    }
}

 

2. 컨테이너 실행

public class UserDaoTest
	public static void main(String[] args) throws ClassNotFoundException, SQLException {
    
    	ApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
        UserDao dao = context.getBean("userDao", UserDao.class);
        ....
}

 

3. 컨테이너(애플리케이션 컨텍스트) 동작 방식 및 장점

동작방식

  1. @Configuration이 붙은 설정파일을 읽고 @Bean이 붙은 메소드를 가져와 빈 목록을 만들어둔다
  2. getBean의 userDao (@Bean이 붙은 userDao 메소드)를 실행하고 반환 오브젝트를 UserDao.class에 주입한다.

장점

  1. 클라이언트는 구체적인 팩토리 클래스를 알 필요가 없다. 직접 Factory클래스를 구현하게 되면 userDao가 많아질수록 구현해야할 Factory가 많아지게되는데 그럴 필요가 없다.
  2. 빈의 생성 뿐만 아니라, 빈의 생성 방식, 시점, 인터셉팅 등 다양한 기능을 수행할 수 있다.
반응형
반응형

1. 난감한 DAO 만들기

다음과 같이 사용자 정보를 저장할 User 클래스를 만든다 

public class User {
	String id;
	String name;
	String password;
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}

 

이후 Mysql을 사용하여 id, Name, Password를 가진 테이블을 생성한다.

이후 다음과 같은 UserDao를 만든다. 사용자 생성, 조회 메소드를 가진 Dao 이다.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class UserDao {

	public void insertUser(User user) throws ClassNotFoundException, SQLException {
		Class.forName("com.mysql.jdbc.Driver");
		Connection c = DriverManager.getConnection("jdbc:mysql://localhost/test?serverTimezone=UTC", "root", "1234");
		PreparedStatement ps = c.prepareStatement("INSERT INTO USERS(ID, NAME, PASSWORD) values(?,?,?)");
		ps.setString(1,user.getId());
		ps.setString(2,user.getName());
		ps.setString(3,user.getPassword());

		ps.executeUpdate();
		
		ps.close();
		c.close();
	}
	
	public User getUserList(String id) throws ClassNotFoundException, SQLException {
		Class.forName("com.mysql.jdbc.Driver");
		Connection c = DriverManager.getConnection("jdbc:mysql://localhost/test?serverTimezone=UTC", "root", "1234");
		PreparedStatement ps = c.prepareStatement("SELECT * FROM USERS WHERE id = ?");
		ps.setString(1,id);

		ResultSet rs = ps.executeQuery();
		rs.next();
		User user = new User();
		user.setId(rs.getString("id"));
		user.setName(rs.getString("name"));
		user.setPassword(rs.getString("password"));
		
		rs.close();
		ps.close();
		c.close();
		
		return user;
	}
}

메인 코드를 다음과 같이 작성하면 정상적으로 동작하는것을 확인할 수 있다. 하지만. 이는 함수마다 connection을 반복적으로 진행하는 좋지 못한 방법이다. 만약 1000개의 함수가 존재하는데 비밀번호가 바뀐다면 1000번 수정해야한다. 따라서 DAO의 분리를 통해 다음과 같이 수정해보자.

 

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class UserDao {

	public void insertUser(User user) throws ClassNotFoundException, SQLException {
		Connection c = getConnection();
        ...
	}
	
	public User getUserList(String id) throws ClassNotFoundException, SQLException {
		Connection c = get Connection();
        ...
	}
    private Connection getConnection() throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");
		Connection c = DriverManager.getConnection("jdbc:mysql://localhost/test?serverTimezone=UTC", "root", "1234");
        return c;
    }
}

이렇게 된다면 getConnection함수를 호출하여 사용하므로 이전과 같은 문제를 해결할 수 있다.

 

이러한 Dao클래스를 외부에서 가져다 사용한다고 하자, A회사 B회사가 각기 다른 DB를 사용하고 Connection을 독자적으로 적용하고싶다고 하자 어떻게 제공할 수 있을까?

 

상속을 통한 확장

다음과 같이 상속을 통해 확장할 수 있다. A,B회사는 각각 UserDao를 상속받고 getConnection을 오버라이딩하여 Dao클래스를 만들 수 있다.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public abstract class UserDao {

	public void insertUser(User user) throws ClassNotFoundException, SQLException {
		Connection c = getConnection();
        ...
	}
	
	public User getUserList(String id) throws ClassNotFoundException, SQLException {
		Connection c = get Connection();
        ...
	}
    public abstract Connection getConnection() throws ClassNotFoundException, SQLException {
		//추상메소드로 구현하여 구현을 서브클래스에서 담당한다
    }
}

 

이렇게 해결한 Dao에도 단점이 있다 상속을 받았다 보니 다른 상속을 받을 수 없다는 단점과, 슈퍼클래스와 하위클래스의 긴밀한 결합이다. 예를들어 슈퍼클래스를 수정하면 모든 서브 클래스를 수정하거나 다시 개발해야할 수 있다.

 

따라서 이를 별도의 클래스로 분리하여 사용하는 방법을 이용한다. 다음과 같이 Connection부분을 별도의 클래스로 분리한다.

 

이후 다음과 같이 수정한다. 이렇게 수정하게되면 connection, dao부분이 분리가 되긴 하였으나, UserDao가 SimpleConnectionMaker에 종속 되어 있다 보니, A,B회사에서 Connection부분을 확장하기에 적합하지 않다. 

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class UserDao {
	private SimpleConectionMaker simpleConnectionMaker;
    
    public UserDao(){
    	simpleConnectionMaker = new SimpleConnectionMaker();
        //생성자를 통해 다음과 같이 만들어놓고 함수에서 사용하도록 하게 한다.
    }
    
	public void insertUser(User user) throws ClassNotFoundException, SQLException {
		Connection c = simpleConnectionMaker.makeNewConnection();
        ...
	}
	
	public User getUserList(String id) throws ClassNotFoundException, SQLException {
		Connection c = simpleConnectionMaker.makeNewConnection();
        ...
	}
}

 

따라서 인터페이스를 활용하면 다음과 같이 해결할 수 있다.

ConnectionMaker interface

public interface ConnectionMaker {
	
    public Connection makeConnection() throws ClassNotFoundException, SQLException;
}

 

ConnectionMaker구현 클래스

public class AConnectionMaker implements ConnectionMaker {
	...
    
    public Connection makeConnection() throws ClassNotFoundException, SQLException {
    	// A회사의 Connection을 생성하는 코드
    }
}

 

UserDao

public class UserDao {
	private ConnectionMaker connectionMaker;
    
    public UserDao(){
    	connectionMaker = new AConnectionMaker();
    }
    
    public void insert.....
    
    public User get.....
    
}

다음과 같이 구현하게 된다면 B사도 Interface에 맞는 클래스만 구현주면 되므로 UserDao부분을 고칠 이유가 사라진다 하지만 AConnection 클래스의 생성자를 호출해서(connectionMaker = new AConnectionMaker();) 오브젝트를 생성하는 부분이남아 있으므로 완벽하게 분리되지 않았다.

 

따라서 우리는 new AConnectionMaker로 객체를 생성하는 방법이 아닌 파라미터로 객체를 넘겨 받아 이를 구현하도록 하겠다.

UserDao 생성자부분

public UserDao(ConnectionMaker connectionMaker) {
	this.connectionMaker = connectionMaker;
}

 

main클래스

import java.sql.SQLException;

public class UserDaoTest {

	public static void main(String[] args) throws ClassNotFoundException, SQLException {
		// TODO Auto-generated method stub
		ConnectionMaker connectionMaker = new DConnectionMaker(); //UserDao부분이 아닌 main에서 객체 생성
        
        UserDao dao = new UserDao(connectionMaker); //객체 주입
    }

}

 

이렇게 이용하게 된다면 main 클래스만 수정하고 UserDao는 완벽하게 분리되게 된다. 공부를 하면서 Dao를 분리시키는 작업이 의존성 주입이랑 비슷하다는것을 알게 되었다.

반응형
반응형

1. 정의

적절한 인증과정이 없이 중요정보(계좌, 개인정보 등)을 열람 할 때 발생하는 보안 약점이다.

 

2. 안전한 코딩 방법

  • 클라이언트의 보안검사를 우회하여 서버에 접근하지 못하도록 한다.
  • 중요한 정보가 있는 페이지는 재 인증이 적용되도록 한다.

3. 예시

재 인증 없이 계좌 이체를 진행하는 예시.

 

재인증을 통한 안전한 예시

위 예시는 실제 아래처럼 작동한다. 만약 중요기능 ex) 회원정보수정, 계좌 이체 같은 중요 기능을 수행할 때 다음처럼 비밀번호를 재인증하는 화면이 사용자에게 주어지고

사용자는 비밀번호를 올바르게 입력했다면, 서버에서는 기존 세션에 있던 정보와 비교하여 유효한 정보인지 검증하는 절차를 가진다.

 

 

다음의 예시는 중요정보(사원의 연봉 정보)를 열람하기 전에 열람 권한이 있는지 먼저 권한 테이블을 조회하여 확인한 후 조회를 허용한다. 적절한 자격이 있는 사용자만 이를 열람할 수 있게한다. 중요함수 전 인증 함수를 수행하여 유효한지 확인하는 방식이다.

사용자가 isStaff란 함수를 구현해 놓고 이를 다음과 같이 사용하여 검증한다.

 

4. 추가적인 방법

spring security를 이용한 권한 확인방법 다음과 같이 권한에 따라 유저가 이용할 수 있는 페이지를 구분하여 권한 검사를 진행할 수 있다.

<intercept-url pattern="/login/loginForm.do" access="permitAll" />
<intercept-url pattern="/home.do" access="permitAll" />
<intercept-url pattern="/admin/**" access="hasRole('ADMIN')" />
<intercept-url pattern="/**" access="hasAnyRole('USER, ADMIN')" />
반응형
반응형

1. 정의

원격으로부터 소스코드 또는 실행파일을 무결성 검사 없이 다운로드 받고 이를 실행할경우 발생하는 변조, DNS spoofing 또는 전송시의 코드 변조 등의 방법을 이용하여 공격자가 악의적으로 파일이나 코드를 변경할 수있다.

 

2. 안전한 코딩 방법

암호화하여 코드나 파일을 제공하고 이를 클라이언트가 검증하도록 한다.

 

3. 예시

다음은 URLClassLoader를 사용하여, 원격의 파일을 다운로드하는 예제이다. 다운로드 대상 파일에 대한 무결성 검사를 수행하지 않을 경우, 파일변조로 인한 피해가 발생할 수 있다.

공개키 방식의 암호 알고리즘을 통하여 전송파일을 암호화하고, 사용자는 이를 복호화 하여 변조 유무를 판단한다.

공개키 방식이란?

공개키 암호화 방식에는 비밀키와 공개키가 존재하는데 비밀키는 서버 쪽에 가지고 있으며 공개되어서는 안 되고, 공개키는 누구에게나 공개되어도 괜찮은 키이다. 먼저 클라이언트는 공개키를 통해 텍스트를 암호화하고 이는 비밀키로만 복호화할 수 있다 따라서 다른 사람이 정보를 가로채더라도 비밀키만 안전하다면 괜찮은 방식이다. (예시는 공개키를 통해 복호화함)

 

다음의 예에서는 파일을 다운로드할 때, 파일의 무결성에 대한 검사를 하지 않고 있다. 따라서 인증된 사이트로 위장하거나, 중간에 파일의 내용을 악의적으로 바꿀 경우, 이에 대하여 대처를 할 방법이 없다.

 

파일의 내용이 공개되어서는 안 될 경우는 모든 파일을 암호화 하지만, 공개되어도 괜찮은 경우네는 체크섬을 이용하여 파일의 내용이 변경되었는지 확인하는것이 더 효과적이다. 다음은 체크섬의 예시이다.

 

체크섬이란?

네트워크를 통해 전달된 값이 변경되었는지를 검사하는 값으로 무결성을 제공합니다.

전달되는 값이 중간에 오류 또는 해킹을 통해서 변질되는경우를 대비해 체크섬값을 같이 보내어 무결성을 확인합니다.

 

1. 수신측에서 IP헤더를 16비트씩 나눈다.

 

2. 나눈 비트중 체크섬은 메시지를 보낸쪽에서 체크섬을 구해서 포함시켜 보낸 값이므로 이 값으로 내가 구한 체크섬값과 비교하여 사용한다 그림에서 b1e6을 제외한 나머지 값을 다 더한다.

 

3. 캐리 값이 발생하였다면 더한다.

 

4. 1의 보수를 취한다.

 

5. 구한값과 전달받은 값을 비교한다.

반응형

+ Recent posts