Spring 숙련 강의
오늘도 어김없이 밀린 TIL 써보겠습니다.
@Controller, @RestController@Service@RepositoryRequestDtoResponseDtoDBMS의 주요 기능
데이터 정의
데이터 관리
데이터 보안
트랜잭션 관리
백업 및 복구
동시성 제어
DBMS의 종류
1. 관계형 DMBS(RDBMS)
가장 많이 사용하는 데이터베이스테이블 (Table)
관계 (Relationships)
SQL (Structured Query Language)
키 (Keys)
트랜잭션(Transaction)
정규화 (Normalization)
데이터 무결성 (Data Integrity)
인덱스 (Index)
MySQL
PostgreSQL
Oracle Database
Microsoft SQL Server
관계형 데이터베이스 관리 시스템(RDBMS)에서 데이터를 정의, 조작, 제어, 조회하기 위해 사용되는 표준 프로그래밍 언어
표준 API
ex) Database 종류가 바뀌어도 쿼리문이 실행된다. MySQL → ORACLE
데이터베이스 연결
SQL 쿼리 실행
Prepared Statement
결과 집합 처리(Result Set)
ex) 데이터를 조회하고 결과를 Java 객체로 매핑할 수 있음
트랜잭션 관리
public class StatementExample {
public static void main(String[] args) {
try {
// MySqlDriver 파일을 라이브러리에 추가한다.
// Driver연결
Class.forName("mysql.jdbc.driver.MySqlDriver");
// Database와 연결(계정 접속)
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/mydatabase", "username", "password");
// Statement 인스턴스 생성
Statement statement = connection.createStatement();
// String SQL Query
String query = "SELECT * FROM MEMBER WHERE NAME = 'wonuk'";
// Query 실행 -> 결과는 ResultSet으로 반환된다.
ResultSet rs = statement.execute(query);
// 결과 처리
while (rs.next()) {
// 결과 처리 로직
}
// 연결을 수동으로 끊어줘야한다.
rs.close();
statement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
? 를 사용하여 preCompile 하여 미리 준비해놓고 Query문을 파라미터 바인딩 후 실행하고 결과를 반환받음 public class PreparedStatementExample {
public static void main(String[] args) {
try {
// [Mysql.jdbc.driver.MysqlDriver] 파일을 라이브러리에 추가한다.
Class.forName("mysql.jdbc.driver.MysqlDriver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/mydatabase", "username", "password");
String query = "SELECT * FROM employees WHERE department = ?";
PreparedStatement preparedStatement = connection.prepareStatement(query);
// 값을 설정
preparedStatement.setString(1, "HR");
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
// 결과 처리 코드
}
resultSet.close();
preparedStatement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
?로 대체하고 파라미터 바인딩을 통해 쿼리를 삽입 즉, 사용자 입력을 직접적으로 쿼리에 삽입하지 않음악의적인 사용자가 애플리케이션에서 입력 데이터를 이용하여 SQL 쿼리를 조작하고 데이터베이스에 무단 접근하거나 데이터를 변조하는 공격
SQL Injection 종류
ex)
# 아이디를 입력하는 email에 WONUK or 1=1 #를 입력
SELECT * FROM MEMBER
WHERE email = 'WONUK or 1=1 #' AND password = 'tutor';
// 검증
public String login(String id, String password) {
// 매개변수 id, password 가 문제없는지 검증하는 로직 Escape 등
String query =
"SELECT * FROM MEMBER WHERE ID = " + id + "AND PASSWORD = " + password;
return query;
}
악성 스크립트를 웹사이트에 주입하는 Code Injection 기법 중 하나
공격자가 웹 어플리케이션에 보낸 악성 코드가 다른 사용자에게 전달될 때 발생
XSS의 종류
공격자가 취약점이 있는 Web Application에 악성 스크립트를 영구적으로 저장하여 다른 사용자에게 전달하는 방식
ex) 게시판(HTML) 글 작성
<script>alert(document.cookie);</script>
해당 script를 게시글에 삽입하면 HTML로 구성되어 있기 때문에 해당 스크립트가 조회하는 사용자에게 실행된다.
외부 링크 페이지로 이동시킴
ex) 메일 내 첨부된 링크 클릭 → 가짜 사이트로 연결
해결방법
애플리케이션에서 데이터를 영구적으로 저장하고 관리하기 위해 데이터베이스와 같은 저장소와의 상호 작용을 단순화하는 소프트웨어 도구
public class PreparedStatementExample {
public static void main(String[] args) {
try {
// ojdbc6.jar[oracle.jdbc.driver.OracleDriver] 파일을 라이브러리에 추가한다.
Class.forName("oracle.jdbc.driver.OracleDriver");
// 1. Connection
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/mydatabase", "username", "password");
String query = "SELECT * FROM employees WHERE department = ?";
// 2. Statement
PreparedStatement preparedStatement = connection.prepareStatement(query);
// 값을 설정
preparedStatement.setString(1, "HR");
// 3. ResultSet
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
// 결과 처리 코드
}
resultSet.close();
preparedStatement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Connection과 같은 공유 자원을 제대로 반환하지 않으면 한정된 시스템 자원(CPU, Memory)에 의해 서버가 다운되는 등의 문제가 발생
SQL Query를 개발자가 직접 작성
ex)
// 1. XML OR Gradle에 Spring JDBC 의존성 추가
// 2. application.properties OR application.yml에 데이터베이스 연결 설정
@RestController
public class MemberController {
private final MemberRepository memberRepository;
public MemberController(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@GetMapping("/members")
public List<Member> findById(Long id) {
return memberRepository.findById(id);
}
}
// Member Object
public class Member {
private Long id;
private String name;
private int age;
// Getter and Setter methods
}
@Repository
public class MemberRepository {
private final JdbcTemplate jdbcTemplate;
public MemberRepository(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// Member 객체로 리턴한다.
public List<Member> findById(Long id) {
String query = "SELECT * FROM MEMBER WHERE id = " + id;
return jdbcTemplate.query(query, (rs, rowNum) -> {
Member member = new Member ();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
member.setAge(rs.getInt("age"));
return member;
});
}
}
// User 클래스
public class User {
private Long id;
private String userName;
private String email;
// Getter and Setter methods
}
// Mapper Interface 생성 : SQL 쿼리와 매핑을 정의하는 인터페이스
public interface UserMapper {
User getUserById(Long id);
}
public class Main {
public static void main(String[] args) {
String resource = "mybatis-config.xml";
try (Reader reader = Resources.getResourceAsReader(resource)) {
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.getUserById(1L);
System.out.println(user.getId() + ", " + user.getUsername() + ", " + user.getEmail);
}
} catch (IOException e) {
e.printStackTrace();
}
}
} Mapper XML SQL 쿼리와 객체 매핑을 정의<mapper namespace="com.example.UserMapper">
<select id="getUserById" resultType="com.example.User">
SELECT id, userName, email
FROM users
WHERE id = #{id}
</select>
</mapper> MyBatis 설정 파일 작성<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mydb" />
<property name="userName" value="userName" />
<property name="password" value="password" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/example/UserMapper.xml" />
</mappers>
</configuration>SQL을 직접 다룸
특정 DB에 종속적으로 사용하기 쉬움
→ 다른 DB를 사용하면 쿼리도 변경해야할 가능성이 높음
테이블마다 비슷한 CRUD SQL, DAO(Data Access Object) 개발이 반복 → 코드 중복
테이블 필드가 변경될 시 이와 관련된 모든 DAO의 SQL문, 객체의 필드 등을 수정해야 함
객체와의 관계는 사라지고 DB에 대한 처리에 집중하게 됨
→ SQL 의존적인 개발
객체지향으로 설계된것을 관계형 DB에 저장하기란 어려움
테이블에 저장한 데이터를 다시 객체화 하는 것도 어려움
// 1. 객체 안의 객체
public class Member {
// 필드들..
private Team team;
} -> ERD?
// 2. 상속 구조
public class Member extends Person {
// 필드들..
} -> ERD?
// 3. extends, implements
public class Member extends Person implements Workable {
// 필드들..
} -> ERD?
객체지향 : 캡슐화, 추상화, 상속, 다형성 → 객체 중심
관계형 데이터베이스(RDB) → 데이터 중심의 구조
각각 지향하는 목적이 다르기 때문에 사용 방법과 표현 방식에 차이가 있음
SQL JOINS 사진 출처 : https://medium.com/@aakriti.sharma18/joins-in-sql-4e2933cedde6
오늘은 내일 쉬니까 강의를 정말 열심히 들었고 TIL도 많이 따라잡고 있다. 조금만 더 힘내보자! 주말에 쉬면서 복습도 쉬엄쉬엄 하고 TIL도 조금 쓰고 회복 잘해서 다음 주도 열심히 해보는 걸로~ (☆▽☆)