1. 알고리즘
1.1 프로그래머스 - k번째 수
•
주어진 배열을 command 배열의 구간 만큼 자르고,
•
정렬한 후,
•
정렬한 배열의 k번째의 수를 출력해야 하는 문제이다.
Code
Arrays.copyOfRange()를 활용한 코드
PriorityQueue를 활용한 코드
1.2 우선순위 큐 (Priority Queue)란?
•
일반적으로 큐는 데이터를 일시적으로 쌓아두기 위한 자료구조
•
FIFO(First-In First-Out)의 구조 → 선입선출의 구조
•
그러나, 우선순위 큐는 먼저 들어온 순서대로 데이터가 나가는 것이 아님
◦
우선순위를 먼저 결정하고, 우선순위가 높은 요소가 먼저 나가는 자료구조
•
heap을 이용하여 구현하는 것이 일반적
2. 토비의 스프링
2.1 StatementStrategy
코드
2.2 AddStrategy
코드
2.3 DataSource Interface
DataSource란?
•
db와 관계된 커넥션 정보를 담고 있으며, 빈으로 등록하여 인자로 넘겨준다
◦
이 과정을 통해, Spring은 Datasource로 Db와의 연결을 획득한다.
•
코드로 DataSource의 활용법을 살펴보자.
•
아래 코드는 빈을 생성하고 관계를 맺어주는 UserDaoFactory 클래스이다.
package com.likelion.dao;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
import javax.sql.DataSource;
import java.util.Map;
@Configuration
public class UserDaoFactory {
@Bean
public UserDao awsUserDao() {
return new UserDao(awsDatasource()); //host가 aws인 db 사용 시, awsDatasource() 주입
}
@Bean
public UserDao localUserDao(){
return new UserDao(localDatasource()); //host가 local인 db 사용 시, localDatasource() 주입
}
//awsDataBase Connection 설정
@Bean
public DataSource awsDatasource(){
Map<String, String> env = System.getenv();
SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriverClass(com.mysql.cj.jdbc.Driver.class); //드라이버 세팅
dataSource.setUrl(env.get("DB_HOST"));
dataSource.setUsername(env.get("DB_USER"));
dataSource.setPassword(env.get("DB_PASSWORD"));
return dataSource;
}
//local Database Connection 설정
@Bean
public DataSource localDatasource(){
SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriverClass(com.mysql.cj.jdbc.Driver.class);
dataSource.setUrl("localhost");
dataSource.setUsername("root");
dataSource.setPassword("12345678");
return dataSource;
}
}
Java
복사
public class UserDao {
private DataSource dataSource; // DataSource를 의존하게 변경
public UserDao(DataSource dataSource) {
this.dataSource = dataSource; // 생성자도 변경
}
public void jdbcContextWithStatementStrategy(StatementStrategy stmt) throws SQLException {
Connection c = null;
PreparedStatement pstmt = null;
try {
c = dataSource.getConnection(); // datasource를 사용하게 변경
/.../
}
}
}
Java
복사
2.4 Dependency Injection 시, final 키워드를 썼을 때 이점
•
위의 코드 UserDao를 다시 살펴봅시다.
public class UserDao {
private final DataSource dataSource; // final 키워드로 dataSource 선언
public UserDao(DataSource dataSource) {
this.dataSource = dataSource; // factory에서 호출해서, 조립이 완료됨 -> 바뀔 여지가 없음
}
public void jdbcContextWithStatementStrategy(StatementStrategy stmt) throws SQLException {
Connection c = null;
PreparedStatement pstmt = null;
try {
c = dataSource.getConnection();
/.../
}
}
}
Java
복사
•
final을 썼을 때 이점
◦
신뢰성 - 불변이기 때문에 변화를 고려하지 않아도 됨
◦
메모리를 적게 씀 - 바뀔 여지가 없기 때문에 바뀌는데 필요한 메모리 할당이 필요 없음
◦
DI를 하고 나서 DataSource가 바뀌는 경우 - 무슨 일이 일어날지 예측이 안됨
•
final을 쓰는 이유
◦
스프링에서 DI 되었다면 이미 Factory에서 조립이 끝난 상태이므로 변화하지 않는게 좋다
◦
변화하지 않으므로 final로 선언해주는 게 좋다.
▪
메모리 사용에 유리하고 신뢰성을 보장하기 때문
2.5 익명 클래스 적용하기
•
우리는 part 2.1에서 StatementStrategy 인터페이스를 하나 생성하였습니다.
•
이 인터페이스는 PreparedStatement를 생성하는 역할을 가집니다.
•
만약 PreparedStatement 인터페이스를 구현해야 하는 구현 클래스가 여러개라면, 매번 이 인터페이스를 구현해야 하는 구현체 클래스들을 생성해주어야 합니다.
◦
만약, 이 구현체 클래스(ex. DeleteAllStrategy)가 하나의 모듈에서만 사용된다면, 굳이 클래스로 만들 필요가 없을 것입니다.
◦
이는, 익명 클래스로 해결이 가능합니다.
익명 클래스 도입 전 코드 (deleteAll())
익명 클래스 도입 후 코드 (deleteAll())
add() 메소드에도 익명 클래스를 적용하자.
익명 클래스 도입 후 add() 메소드
2.6 JdbcContext로 분리 (234pg)
•
JDBC의 일반적인 작업 흐름을 담고 있는 jdbcContextWithStatementStrategy()는 다른 DAO에서도 사용 가능하다.
•
따라서, jdbcContextWithStatementStartegy()를 UserDao 클래스 밖으로 독립시켜서 모든 DAO가 사용할 수 있게 해보자.
public class JdbcContext {
private DataSource dataSource;
public JdbcContext(DataSource dataSource) {
this.dataSource = dataSource; //dataSource에 의존
}
public void workWithStatementStrategy(StatementStrategy stmt) throws SQLException {
Connection c = null;
PreparedStatement pstmt = null;
try {
c = dataSource.getConnection(); //dataSource 오브젝트에게 커넥션 생성 위임
pstmt = stmt.makeStatement(c); //StatementStartegy 인터페이스에게 sql 쿼리문 생성 위임
// Query문 실행
pstmt.executeUpdate();
} catch (SQLException e) {
throw e;
} finally {
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
}
}
if (c != null) {
try {
c.close();
} catch (SQLException e) {
}
}
}
}
}
Java
복사
•
위의 코드는 jdbcContextWithStatementStrategy() 메소드를 JdbcContext 라는 독립된 클래스로 분리한 것이다.
•
이제, UserDao 클래스 뿐만 아니라 다른 타입의 dao 클래스에서도 커넥션 생성 및 쿼리문 생성, 실행 등의 jdbc 일반적인 작업 흐름을 담고 있는 메소드를 실행시킬 수 있다.
2.7 UserDao와 JdbcContext
•
기존의 UserDao는 ConnectionMaker라는 커넥션 생성의 역할을 가지고 있는 인터페이스에만 의존했습니다.
•
그러나, 커넥션 생성과 쿼리문 생성, 실행 등의 jdbc의 일반적인 작업 흐름을 담당하는 클래스인 JdbcContext를 2.6 part에서 생성하였습니다.
•
따라서, JDBC를 활용해야 하는 UserDao 클래스는 JdbcContext 클래스와 종속성을 가지게 됩니다.
public class UserDao {
private final JdbcContext jdbcContext;
public UserDao(DataSource dataSource) {
this.jdbcContext = new JdbcContext(dataSource);
}
public void add(final User user) throws SQLException {
// DB접속 (ex sql workbeanch실행)
// 템플릿 콜백 패턴 -> workWithStatementStrategy 메소드내에 tatementStrategy의 구현체 내용 구현
jdbcContext.workWithStatementStrategy(new StatementStrategy() {
@Override
public PreparedStatement makeStatement(Connection conn) throws SQLException {
PreparedStatement pstmt = null;
pstmt = conn.prepareStatement("INSERT INTO users(id, name, password) VALUES(?,?,?);");
pstmt.setString(1, user.getId());
pstmt.setString(2, user.getName());
pstmt.setString(3, user.getPassword());
return pstmt;
}
});
}
Java
복사
•
2.6의 과정과 2.7의 리팩토링을 거치면, UserDao의 의존관계는 위의 그림과 같게 됩니다.
강의를 통해 구현했었던 전략 패턴이 적용된 코드
2.8 JdbcContext와 DataSource의 관계 설정을 생성자에서 한 이유
public class UserDao {
private final JdbcContext jdbcContext;
public UserDao(DataSource dataSource) {
this.jdbcContext = new JdbcContext(dataSource); //구체 클래스와의 관계가 설정에 직접 노출됨
}
public void add(final User user) throws SQLException {
// DB접속 (ex sql workbeanch실행)
// 템플릿 콜백 패턴 -> workWithStatementStrategy 메소드내에 StatementStrategy의 구현체 내용 구현
jdbcContext.workWithStatementStrategy(new StatementStrategy() {
@Override
public PreparedStatement makeStatement(Connection conn) throws SQLException {
PreparedStatement pstmt = null;
pstmt = conn.prepareStatement("INSERT INTO users(id, name, password) VALUES(?,?,?);");
pstmt.setString(1, user.getId());
pstmt.setString(2, user.getName());
pstmt.setString(3, user.getPassword());
return pstmt;
}
});
}
Java
복사
2.9 Template Callback 적용 (247 pg)
•
executeQuery ⇒ Query를 받아서 PreparedStatement 를 만든 후 실행하는 기능
•
public void executeSql(String sql) throws SQLException {
this.workWithStatementStrategy(new StatementStrategy() {
@Override
public PreparedStatement makePreparedStatement(Connection connection) throws SQLException {
return connection.prepareStatement(sql);
}
});
}
Java
복사
public void deleteAll() throws SQLException {
this.jdbcContext.executeSql("delete from users");
}
Java
복사
•
템플릿 콜백 패턴이 적용된 구조를 그림으로 나타낸 것이다.
◦
add() 메소드 내에서 JdbcContext 클래스의 workWithStatementStragy() 메소드를 호출한다. 이 때, 템플릿 콜백 패턴을 적용하여, StatementStrategy 인터페이스의 구현 클래스를 구현해준다.
2.10 JdbcTemplate 적용 (262pg)
JDBC Template란?
Spring JDBC 접근 방법 중 하나로, 내부적으로 Plain JDBC API를 사용하지만 아래의 문제점을 해결한 스프링에서 제공하는 클래스
•
Plain JDBC의 문제점
◦
쿼리를 실행하기 전과 후에 많은 코드를 작성해야 한다. (연결생성,명령문 등등)
◦
예외 처리 코드와 트랜잭션 처리등에 시간과 자원이 소모
◦
이러한 문제점을 보완하여 생겨난 것이 Spring JDBC
JDBCTemplate이 제공하는 기능
실행 : insert나 Update와 같이 DB의 데이터에 변경이 일어나는 쿼리를 수행하는 작업
조회 : select를 이용해 데이터를 조회하는 작업
배치 : 여러 개의 쿼리를 한 번에 수행해야 하는 작업
jdbcTemplate을 사용하면 커넥션 연결/종료와 같은 세부 작업을 개발자가 직접 처리 하지 않아도 된다.
JDBCTemplate을 활용한 UserDao 클래스