어제 숙제 관련 질문을 했다. 숙제의 4번 문제는 3개의 테이블을 조인 한 후 특정 테이블의 조건에 맞춰 SELECT해서 보여줘라. 였는데 이 숙제를 하면서 들었던 의문 중 하나가 테이블을 조인한 결과값을 새로운 테이블의 형태로 갖는건은 불가능한가? 였다. 자바에서 사용했던 방식인 A리스트에서 1조건을 만족하는 인덱스값만 뽑아와 새로운 B리스트에 넣는 방식처럼.
이런 식으로 작업을 하면 B리스트에서 모든 작업을 할 수 있기에 너무 편했다.
위의 작동방식 A, B, C 테이블의 JOIN한 결과값을 새로운 테이블로 만들어서 볼 수는 없냐고 질문했다. 하지만 그것은 불가능하다고 한다. JOIN된 값은 그저 조회만 가능할 뿐 수정이나 삭제등이 불가능하다고 했다.
하지만 여러 테이블을 JOIN해서 할경우 ENTITY를 이용해서 정보를 가져와서 사용한다고 말씀하셨다. 아직 ENTITY가 어려워서 잘 이해를 못했다. ENTITY에 대해 좀 더 알아보자.
지난 1주차에서 사용했던 Controller는 지나치게 많은 업무를 한 클래스가 담당하고 있다. 많은 가짓수의 API처리를 다뤄야 하기 때문. 따라서 변동/수정사항이 생기면 대처하기가 어렵다. 이에 개발자들은 3Layer - Architecture방식을 고안했다.
3Layer - Architecture는 서버에서 처리해야 하는 업무가 대부분 비슷하다는 것을 깨달은 개발자들이 만든 방식이다. Controller, Service, Repository 3개로 분리되어있다.
[Controrller]
Contoller는 클라이언트에게 요청받은 로직을 처리하는 부분이다. 또한 데이터가 필요하다면 Service에게 요청을 넘겨주는 파트. 이후 Service에서 처리된 결과값을 클라이언트에게 Response 해주는 역할이다. 간단히 클라이언트와 가장 직접적으로 통신하는 파트이자, 비즈니스 로직이 들어있는 Service를 클라이언트에게 전달 하는 역할이라고 할 수 있다.
[Service]
서비스는 Controller에게 전달받은 요구사항을 처리(비즈니스 로직)하는 파트이다. 이때 DB의 데이터가 필요하다면 Repository에게 데이터를 받아온다.
[Repository]
Repository는 관리를 하는 파트.
그중 우리는 Service 파트에 가장 많이 신경을 쓰고 공부를 한다고 한다.
그리고 배운 Ioc와 DI Spring을 처음 접하고 정의에 대해 공부하면서 Ioc를 접했던 기억이 있다. Ioc는 설계 원칙에 해당하고, DI는 디자인 패턴에 해당한다. 간단히 설명해 DI라는 디자인 패턴을 이용해 Ioc의 설계 원칙을 지킨다. 정도로 이해했다. 그럼 Ioc는 어떤 설계 원칙일까?
[IOC와 DI]
Ioc란 "Inversion of Control"로서 한국말로 "제어의 역전"이다. 보통 개발자들이 메서드나 객체의 호출 방식을 미리 결정하지만, 제어의 역전이 일어난다면, 호출 방식을 개발자가 외부에서 결정하게 된다. 서블렛의 작동을 보면 개발자가 서블렛을 만들고 배포를 한 후엔 더이상 개발자가 서블렛에 관여할 수 없게된다. 이때는 서블렛이 적절한 객체와 메서드를 호출해서 요청들을 수행하게 되는데 이것을 제어의 역전이라고 볼 수 있다. 이러한 방식은 대부분의 프레임워크에서 사용하는 방식이고, 개발자들은 프레임워크에 필용한 부품들을 개발하고, 조립하는 방식으로 개발을 진행하게 된다.
Ioc가 Spring의 대표적인 특성이지만, Ioc는 스프링만 가지고 있는 특색은 아니다 . 하지만 Spring의 Ioc가 특별한 이유는 바로 위에 언급한 DI 때문. DI는 "Dependency Injection"으로서 한국말로 "의존성 주입"이다. "의존성 주입"이란 제어의 역전이 일어날 때 각 객체간의 관계에 대한 이야기이다.
[DI]
먼저 의존성에 대해 알아보자. 우선 의존성은 강한 의존성과 약한 의존성으로 구분될 수 있다. 강한 의존성의 예시는 다음과 같다.
public class Consumer {
void eat() {
Chicken chicken = new Chicken();
chicken.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.eat();
}
}
class Chicken {
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
여기서 주목해야 할 것은 Consumer 클래스의 eat()메서드는 Chicken 객체를 갖고 있다는 것. 이게 강한 의존성의 특징이다. 그 이유는 만약 Consumer 클래스의 eat()메서드가 Chicken이 아닌 Pizza를 먹게 된다면? Consumer 클래스 자체를 고쳐야 한다. 이 코드를 약한 의존성으로 바꾸려면
public class Consumer {
void eat(Food food) {
food.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.eat(new Chicken());
consumer.eat(new Pizza());
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
Consumer클래스가 위의 강한 의존성과 달라진 점은 eat()메서드에 Food 클래스를 인자를 받는다는것. Food는 인터페이스로서 Chicken 클래스와 Pizza 클래스로 구현이 되었다. 이렇게 구현이 된다면, eat()메서드에는 Chicken 클래스도 들어갈 수 있고, Pizza 클래스도 들어갈 수 있게 된다. 이렇든 자바에서는 인터페이스를 통해 의존성을 낮출 수 있다.
사실 이 부분 강의를 들으면서 조금 쾌감을 느꼈다. 왜냐하면 내가 지난 개인과제(키오스크 과제)를 하면서 Menu 클래스를 부모클래스로 두고, 나머지 상세 메뉴들을 상속받아 작성했다. 그 후 대부분의 메서드에는 Menu클래스를 인자를 받게 했다. 그렇게 하면 모든 상세 메뉴들을 넣을 수 있게 되기 때문. 내가 약한 의존성에 대한 개념은 몰랐지만, 이렇게 해야한다는 것을 알았다. 왜냐하면 그렇게 하지 않으면 코드량이 너무 방대해지고, 쓸데 없는 메서드가 많아진다는 것을 알았기 때문이다. 내가 고민하고 내가 구현한 것을 강의로 듣고, 개념을 확실하게 배우니 정말 빠르게 이해가 됐다.
그렇다면 주입은 무엇일까?
주입은 간단히 얘기하면 setter메서드를 사용하듯이 객체 안의 값을 어떻게 넣느냐? 이다. 우리가 보통 많이 사용하는 방식은 3가지이다.
첫번째는 생성자를 이용한다. 기본적으로 생성자를 구현할 때 필드값들을 인자로 받아 필드값들을 초기화 하는데 이것은 생성자를 통한 주입방식이라고 할 수 있다.
두번째는 필드에서 직접 주입. 이방식은 필드값의 접근지정자가 적절히 돼있으면 사용 가능한 방식일 것 같은데
class.field = new class()의 방식처럼 그냥 바로 꺼내와서 사용하는 방식
세번재는 메서드를 통한 주입. 이방식은 setter()메서드가 작동하는 것과 같은 방식으로 구현된다.
이렇게 주입을 통해 객체 안의 데이터를 넣어주고, 인터페이스를 이용하여 의존성을 낮추게 되면 제어의 흐름이 반대로 이루어진다.
위의 강한 의존성을 갖는 코드의 예시를 보자. 이 방식은 Consumer가 Chicken을 직접 만들어 먹는다고 볼 수 있다. 따라서 제어의 흐름이 Consumer -> Chicken이라고 볼 수 있다. 그 이유는 Consumer 자체에서 Chicken 객체를 가지고, Chicken의 메서드를 사용하기 때문이다. 자신이 외부의 메서드를 호출해서 보내주면 끝난다.
하지만 약한 의존성을 갖는 코드는 반대이다. Consumer 안에는 Food 인터페이스만 존재한다. 따라서 어떤 객체가 들어올지 아직 정해지지 않았다. 그말은 Food를 구체화한 특정 클래스가 Consumer안에서 작동하게 된다는 것. 이때 구체화된 클래스가 Consumer 클래스로 들어오는 것이 주입! 따라서 Consumer는 Food의 결정으로 자신의 메서드 호출을 바꿔준다. 제어의 흐름이 Food-> Consumer로 넘어갔다. 이것을 제어의 역전!이라고 볼 수 있다.
내가 해석한 바로는 제어의 역전은 좀 더 유연한 프로그래밍이다. 또한 상호 관계에 특화돼있다. 강한 의존성은 메서드에 인자를 받는다던지 하는 행위가 없기에 단순한 작업을 하는 방식에는 상관이 크게 없을 것 같다.
하지만 Ioc 설계 원칙을 지킨다면 좀 더 유연하고, 재사용성이 간편해지지만, 상호 의존 관계가 굉장히 올라가면서 나중에 코드를 변경할 때 객체간의 관계를 파악하는 것도 쉽지 않겠다. 라는 생각이 들었다.
물론 Ioc설계 방식이 더 합리적이라고 할 수는 있겠지만, 단순한 코드에서는 강한 의존성을 갖는 코드도 힘을 갖지 않을까? 라고 생각했다.
'공부 > TIL' 카테고리의 다른 글
23.11.06(Springboot_CRUD) (0) | 2023.11.06 |
---|---|
23.11.03 (1) | 2023.11.03 |
23.11.01 (0) | 2023.11.01 |
23.10.31 (1) | 2023.10.31 |
호텔 예약 프로그램 (1) | 2023.10.30 |