스터디 날짜
24.07.05.
스터디 범위
1장. 오브젝트와 의존관계
스프링이 가장 관심을 많이 두는 대상은 오브젝트다. 스프링을 이해하려면 먼저 오브젝트에 깊은 관심을 가져야 한다. 애플리케이션에서 오브젝트가 생성되고 다른 오브젝트와 관계를 맺고, 사용되고, 소멸하기까지의 전 과정을 진지하게 생각해볼 필요가 있다.
💬 스프링은 자바의 기술이 복잡해지면서 잃어버린 객체지향 언어의 본질과 장점을 되살릴 수 있도록 도와주는 도구이다. 스프링은 가장 단순한 객체지향적인 개발 모델, POJO 프로그래밍을 주장한다.
진정한 POJO란,
공통 프로그래밍 모델
IoC/DI
오브젝트의 생명주기와 의존관계에 대한 프로그래밍 모델이다.
유연하고 확장성이 뛰어난 코드를 만들 수 있게 도와주는 객체지향 설계 원칙과 디자인 패턴의 핵심 원리를 담고 있으며 스프링 프레임워크의 근간이 된다.
PSA (서비스 추상화)
구체적인 기술과 환경에 종속되지 않도록 유연한 추상 계층을 두어 이식성이 뛰어나며 유연한 애플리케이션을 만들 수 있게 된다.
AOP
애플리케이션 코드에서 부가적인 기능을 독립적으로 모듈화하는 프로그래밍 모델이다.
💁 DAO(Data Access Object)는 DB를 사용해 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 오브젝트를 말한다.
💬 사용자 정보를 저장할 때는 자바빈 규약을 따르는 오브젝트를 이용하면 편리하다.
✅ 자바빈 규약의 규칙
자바빈은 기본 패키지 이외의 특정 패키지에 속해 있어야 한다.
기본 생성자가 존재해야 한다.
멤버변수의 접근제어자는 private로 선언되어야 한다.
멤버변수에 접근 가능한 getter와 setter 메서드가 존재해야 한다. (3번의 규칙을 지키기 위함)
getter와 setter는 접근제한자가 public으로 선언되어야 한다.
직렬화 되어 있어야 한다. (선택사항)
직렬화란?
참고 링크
Serialize,
직렬화를 위한 인터페이스로 객체를 파일에 저장하거나, 다른 서버로 보내거나 받거나 등의 일을 하기 위해 구현한다.
자바 객체를 serialize하기 위해서는,
java.io.Serializable
인터페이스를 구현하도록 한다. 해당 인터페이스는 멤버변수나 메서드가 존재하지 않는 maker interface이다.
Serialization 특징
Serializable
인터페이스를 구현하면 자식 클래스는 자동으로 Serializable
하다.transient
데이터로 지정하도록 한다.class Member implements Serializable {
transient String password;
String name;
...
}
Serializable
한 객체와 연관되어 있는 객체 또한 Serializable
인터페이스를 반드시 구현해야 한다.class ObjectA implements Serializable {
//ObjectB는 반드시 Serializable을 구현해야 함
ObjectB oj = new ObjectB();
}
Serializable
을 implement하여 직렬화가 가능하다. 해당 객체에 영속성을 부여하기 위해서 사용되는 매커니즘이다.
marker interface란?
참고 링크
💁 인터페이스 내부에 아무 것도 없는 인터페이스로, 객체의 타입과 관련된 정보를 제공해 준다. 컴파일러와 JVM은 이 마커 인터페이스를 통해 객체에 대한 추가적인 정보를 얻을 수 있다.
❓객체의 타입과 관련된 정보를 제공해준다
NotSerializableException
import java.io.*;
public class MarkerInterfaceTest {
public void serializableTest() throws IOException {
File f = new File("test.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(f));
// 이 부분이 객체를 파일로 저장하는 부분
objectOutputStream.writeObject(new SomeObject("kjhoon", "kjhoon0330@snu.ac.kr"));
}
public static void main(String[] args) {
MarkerInterfaceTest t = new MarkerInterfaceTest();
try {
t.serializableTest();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 인터페이스를 구현하지 않은 임의의 오브젝트
class SomeObject {
private String name;
private String email;
public SomeObject(String name, String email) {
this.name = name;
this.email = email;
}
}
class SomeObject implements Serializable {
// Serializlble 인터페이스 구현
// 내부 생략
}
해결된 이유
아래 사진은 ObjectOutputStream
의 writeObject
함수 내부를 보여준다. obj
가 Serializable
의 인스턴스인 경우 NotSerializableException
이 발생되지 않도록 처리하고 있다. 이는 Serializable
이라는 마커 인터페이스가 객체에 추가적인 정보를 주입시키고 있음을 이야기한다.
개발자가 객체를 설계할 때 가장 염두에 둬야 할 사항은 바로 미래의 변화를 어떻게 대비할 것인가이다. 객체지향 설계와 프로그래밍이 이전의 절차적 프로그래밍 패러다임에 비해 초기에 좀 더 많은, 번거로운 작업을 요구하는 이유는 객체지향 기술 자체가 지니는, 변화에 효과적으로 대처할 수 있다는 기술적인 특징 때문이다.
💁 템플릿 메소드 패턴
💁 팩토리 메소드 패턴
💆 관심사의 분리가 가능해졌지만, 상속을 사용하는 단점이 존재한다.
상속을 사용하는 방식 대신, 관심사가 다른 두 부분을 클래스로 분리한다.
다만 이 경우 아래의 문제가 재차 발생한다.
-> 하나의 클래스가 다른 클래스와 그 코드에 종속적이면 자유로운 확장이 힘들다. 어떤 클래스에서 어떤 메서드를 가져와야하는지 일일이 지정하지 않아도 괜찮을 순 없을까?
클라이언트
의 책임으로 전가해서 런타임 시점에 다이내믹한 오브젝트 관계를 가지게끔 하는 것이다.개방 폐쇄 원칙
높은 응집도와 낮은 결합도
팩토리
제어권의 이전을 통한 제어관계 역전
💬 스프링의 핵심을 담당하는 건, 바로 빈 팩토리 또는 애플리케이션 컨텍스트라고 불리는 것이다. 이 두가지는 우리가 만든 DaoFactory가 하는 일을 좀 더 일반화한 것이라고 설명할 수 있다.
빈, Bean
빈 팩토리, Bean Factory
애플리케이션 컨텍스트
@Configuration
public class DaoFactory {
@Bean
public UserDao userDao() {
return new UserDao(connectionMaker());
}
@Bean
public ConnectionMaker connectionMaker(){
return new DConnectionMaker();
}
}
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);
...
}
오브젝트의 동일성과 동등성
동일성
== 연산자동등성
equals() 메서드스프링 컨텍스트로부터 가져온 오브젝트는 매번 동일한 값을 돌려준다.
애플리케이션 컨텍스는 싱글톤을 저장하고 관리하는 싱글톤 레지스트리이다.
왜 스프링은 싱글톤으로 빈을 만들까?
싱글톤 패턴의 한계
대신 스프링은 싱글톤 레지스트리를 제공한다
싱글톤이 멀티스레드 환경에서 서비스 형태의 오브젝트로 사용되는 경우에는 상태정보를 내부에 갖고 있지 않은 무상태 방식으로 만들어져야 한다.
읽기 전용 속성의 정보라면 싱글톤에서 인스턴스 변수로 사용해도 좋다.
물론 단순한 읽기전용 값이라면 static final
이나 final
로 선언하는 편이 낫다.