새로운 내용을 공부할 때
새로운 내용의 공부를 시작할 때 용어의 정의를 이해하지 못하거나 정확하게 알지 못한다면 그 용어가 포함된 문장을 이해하지 못합니다.
작은 단어 하나가 내용을 이해하지 못하게 하기 때문에 용어를 정확하게 이해하는 것이 중요합니다.

3 분 소요

코드 개선과 공부 방향에 대한 생각

토비의 스프링 3.1 책을 읽으면서 자바 개발자로 성장하는 방향에 대해서 생각을 했습니다.

아래 코드는 토비 3.1 책에서 나오는 예제입니다.

문제 코드

public class UserDao {

	public void add(final User user) throws ClassNotFoundException, SQLException {
		System.out.println("User added: " + user.getId());
		Class.forName("com.mysql.cj.jdbc.Driver");

		final Connection con = DriverManager.getConnection("jdbc:mysql://localhost/spring", "root", "1234");
		final PreparedStatement ps = con.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();
		con.close();
	}

	// get users info by id
	// create
	public User get(final String id) throws SQLException, ClassNotFoundException {
		Class.forName("com.mysql.cj.jdbc.Driver");
		final Connection con = DriverManager.getConnection("jdbc:mysql://localhost/spring", "root", "1234");

		// find
		final PreparedStatement ps = con.prepareStatement("select * from users where id = ?;");
		ps.setString(1, id);

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

		rs.close();
		ps.close();
		con.close();

		return user;

	}

	public static void main(String[] args) throws SQLException, ClassNotFoundException {
		UserDao userDao = new UserDao();
		User user = new User();
		user.setId("sungnam1");
		user.setName("Sungnam");
		user.setPassword("password");
		userDao.add(user);

		final User user1 = userDao.get("sungnam1");
		System.out.println(user1);
	}
}

처음에는 코드를 보면서 문제점을 찾아보라는 질문이였습니다.

생각없이 예제 코드를 보면서 문제점 찾기

  1. 회원을 저장하거나 추가하는 요청이 많아질 경우 서버와 데이터베이스 커넥션이 비례해서 증가한다.
  2. 데이터베이스에서 조회와 저장시 매번 새 커넥션 연결 과정이 생긴다.
  3. 데이터베이스 연결에 필요한 정보가 메서드 내에 있어서 변경하려면 모든 소스 코드를 변경해야한다.
  4. 클라이언트가 데이터베이스 연결에 필요한 객체를 의존하는 책임이 있다.
  5. 저장이나 조회 도중 오류가 발생하는 경우 close가 처리되지 않고 리소스가 반납되지 않는다.

자바 개발자로 일하면서 학습한 문제들을 예제 코드에서 찾으려고 했습니다.

그리고 다음 주를 읽는데 생각없이 문제를 찾고 있었고, 문제에 대한 정의도 구체적으로 작성하지 않았다는 것을 배웠습니다.

느낀점

단순히 코드가 깔끔하지 않다.

또는 객체지향적이지 않다는 이유로 문제점이라고 생각했습니다.

  • 왜 이 코드가 문제인지?
  • 이를 개선함으로써 어떤 장점이 생기는지?
  • 장기적으로 유지보수정과 확장성에 어떤 영향을 주는지?

이런 점들을 고민하고 정리하는 과정이 필요했다는 걸 알았습니다.

보완할 점 & 개선 방향

1. 문제 정의를 더 구체적으로 한다.

회원을 저장하거나 추가하는 요청이 많아질 경우 서버와 데이터베이스 커넥션이 비례해서 증가한다.

제가 처음에 작성한 예제 코드를 보면서 작성한 문제점 입니다.

이 문제를 조금 더 구체적으로 작성한다면

  • 성능 문제로 이어질 가능성(커넥션 개수가 늘어나면 DB의 부하가 커지고 응답속도가 느려질 가능성)
  • 자원 관리 문제(Connection이 적절히 반환되지 않으면 리소스 누수가 발생할 수 있음)

이런 식으로 좀 더 실질적인 문제로 구체화를 했어야합니다.

2. 문제를 개선했을 때의 이점를 정의하기

  • Connection Pool을 도입해서 커넥션을 재사용하면 커넥션을 연결하고 연결해제하는 리소스 낭비를 줄일 수 있다.
  • DB 연결 정보를 외부로 분리하면 환경 변화에 유연하게 대처하여 테스트 및 배포 환경에 유연하게 대처할 수 있다.
  • try-with-resources를 사용하면 자동으로 리소스를 반환할 수 있어, 실수로 close()를 안하는 문제로 리소수가 누수가 발생하는 문제를 예방할 수 있다.
  • 클라이언트가 데이터베이스 연결 객체를 직접 관리하지 않고, 의존 관계 책임을 의존성 주입(DI) 을 사용하면 결합도를 낮추고 외부 설정을 통해 유지보수성이 올라간다.

3. 객체지향 설계 원칙과 연관성

문제점을 개선하면 객체지향의 어떤 원칙이 적용되어지는지도 고려할줄 알아야합니다.

  • SRP(단일 책임 원칙)

    데이터베이스 연결 로직이 비즈니스 로직과 분리되어 더 명확한 오브젝트 역할을 가진 코드가 된다.

  • DIP(의존 역전 원칙)

    클라이언트가 Connection 객체를 직접 관리하는 것이 아니라, 데이터소스를 주입받게하여 유연성이 증가한다.

코드를 개선하는 이유를 생각해보는 습관을 길들인다.

단순히 코드가 지저분하다는 이유가 아니라, 아래와 같은 문제가 있어서 코드를 개선하는 리소스가 필요합니다.

  1. 성능 문제 → 매번 DB 연결을 새로 맺으면 리소스 낭비가 발생하고, 커넥션 개수가 많아지면 DB 부하가 커진다.

  2. 유지보수 문제 → DB 접속 정보가 하드코딩되어 있으면 환경 변경이 있을 때 모든 소스를 수정해야 한다.

  3. 리소스 누수 가능성 → close()가 제대로 호출되지 않으면 커넥션이 해제되지 않아 메모리 문제가 발생할 수 있다.

  4. 클라이언트 코드가 DB 연결을 직접 관리 → 의존성이 높아지고, 다른 DB로 변경할 때 코드 수정 범위가 커진다.

  5. 객체지향적 설계 부족 → 비즈니스 로직과 인프라 로직이 분리되지 않아, 코드 재사용성과 확장성이 떨어진다.

이 문제를 개선하면 어떤 이점이 있는가?

  1. Connection Pool을 활용하여 성능 향상

​ • HikariCP 같은 커넥션 풀을 도입하면 커넥션을 재사용할 수 있어 성능이 좋아지고 DB 부하가 줄어든다.

  1. 환경 설정을 분리하여 유지보수성 향상

​ • DB 정보는 application.yml에 따로 설정하고, 코드에서는 이를 주입받도록 한다.

  1. try-with-resources를 활용하여 리소스 누수 방지

​ • 자동으로 close()가 호출되므로 안정성이 높아진다.

  1. 의존성 주입(DI)을 활용하여 유연성 증가

​ • DataSource를 주입받도록 변경하면, 추후 DB 변경이나 Mocking이 쉬워진다.

  1. 객체지향적 설계 원칙을 적용하여 코드 품질 향상

​ • SRP(단일 책임 원칙): DB 연결과 비즈니스 로직을 분리하여 역할을 명확히 한다.

​ • DIP(의존 역전 원칙): Connection을 직접 생성하는 것이 아니라 DataSource 인터페이스에 의존하도록 변경한다.

댓글남기기