코드스테이츠 수강 9주차 1일차에는 Spring FrameWork 기본에 대해 배웟다.
Spring FrameWork이란?
현직자 친구들이 항상 말한게 있다.
"백엔드 갈거면 스프링이다, 부스트캠프에서 스프링 말고 다른거 하면 그거 가지 말고, 직장에 가서도 스프링 말고 이상한거 쓰면 탈출버튼 눌러라"
"스프링이 뭔데? 띠이이이용? 이거?"
"감히 최고존엄 스프링 수령님을 욕보이다니 불벼락을 맛보여주겠다!"
대략 백엔드에서는 스프링이 90퍼는 한다라는 것이라는데, 그래서 이 단원이 중요한것 같다.
일단 스프링 프레임워크에 배우기 전에 프레임 워크가 뭐냐? 할 수 있다.
프레임, 말 그대로 뼈대가 되는것을 뜻한다.
우리가 html을 프론트엔드에서의 기초적인 뼈대라고 말한 것 처럼, 프레임워크도 비슷한 맥락이다.
가구에 빗대서 설명하면, 외관, 골조 등이 될텐데, 서랍장에 비유 하겠다.
우리가 서랍장을 쓸때 서랍을 열고 닫으면서 사용한다.
여기서, 서랍 말고도 서랍을 와리가리 할 수 있게 슬라이드도 달고, 손잡이도 다는데, 기본적으로 서랍장의 뼈대가 있다.
서랍장을 만들 때 먼저, 서랍의 크기, 재질, 몇개의 서랍이 들어가는건 뼈대에 따라 정해진다는 것이다.
이러한 전체적인 구조를 뼈대, 프레임워크라고 한다.
우리는 이 프레임워크에 직접 기능을 추가하거나, 라이브러리를 활용해서 기능을 부여한다.
위의 사진에서 레일과 서랍이 서랍장의 뼈대(프레임워크)에 들어가 기능을 수행하는 (열고 닫게 해주는) 부품(라이브러리) 라고 볼 수 있다.
스프링 프레임 워크는 이러한 일을 자바를 활용해서 뼈대를 만들고, 우리가 라이브러리를 사용해서 기능을 부여 할 수 있는 것이다.
여기서 기억해야 할 것이 뼈대(프레임워크)는 지 생긴대로 노니까 주도권이 뼈대(프레임워크)에 있고, 서랍, 레일 등의 요소(라이브러리)는 우리가 필요로 직접 달아주는 거니까 주도권이 우리(개발자)한테 있다
프레임워크를 사용하면, 효율적으로 코드를 작성할 수 있고
(기본 구조가 짜여 있으니 뼈대부터 다시 만들 일이 없다.)
정해진 규약이 있어서 애플리케이션을 효율적으로 관리가 가능하다.
(프레임워크의 규약에 맞게 코드를 작성하기 때문에 다른사람도, 같은 규약에 따라 작성한다 -> 유지보수가 쉬워진다.)
하지만, 마냥 장점만 있는것은 아니다.
일단 프레임워크를 사용 하려면 해당 프레임워크에 대해 또 공부를 해야한다... 프레임워크마다 조금씩 다르기 때문에 그에 맞춰서 코드를 작성해야 하기 때문이다.
또한, 프레임워크에서 벗어나는 개발이 어렵다.
특정 기능, 구조가 프레임워크 밖에 있는 것이라면 프레임워크를 변경하거나, 예외를 주거나... 말이 쉽지 그냥 못한다고 보면 된다.
스프링 프레임워크는 다른 프레임워크에 비해 장점이 크게 4가지 있다고 한다.
- POJO(Plan Old Java Object)기반의 구성
- DI(Dependency Injection) 지원
- AOP(Aspect Oriented Programming, 관점지향 프로그래밍) 지원
- Java 언어를 사용함으로써 얻는 장점
일단 위의 4번은 JAVA언어의 장점을 그대로 따오는 것이라고 보면 된다.
(변수타이브 메서드 입출력의 타입 명시, 객체지향의 장점...등등)
위의 3개는 아직 모르는게 당연하다.
그냥 위의 3가지 장점이 실무에서(기업에서) 큰 장점으로 다가오며, 스프링이 이 장점들을 가지고 있다 정도로 알고 있으면 된다.
1. POJO (Plain Old Java Object)
위의 그림은 스프링 삼각형이라는 유명한 그림이라고 한다.
위에서 언급한 장점(핵심 개념)들을 삼각형으로 요약 한 것이라고 하는데, 스프링이 저 요소들을 가지고 있다 라고 생각하면 된다.
POJO(Plain Old Java Object)의 뒷부분 JO(Java Object)는 객체지향 프로그래밍을 의미한다.
그러면 PO(Plain Old)는?
순수하게, 이전거 그대로를 말한다.
POJO를 통틀어서 말하면 "순수하게 JAVA로 생성한 객체" 를 의미한다.
이게 뭔소리냐 싶을 수 있는데, 사실 간단하다.
아이스크림을 만든다고 생각하자.
우리가 만약 아이스크림 공장을 운영하는데, 기본 새하얀 아이스크림에 설탕만 가미된 아이스크림을 만들어서 블루베리나 딸기를 가미하여 딸기 아이스크림, 블루베리 아이스크림 등을 만든다고 치자
이때 새하얀 아이스크림은 어떤 아이스크림에도 들어갈 수 있는 순수한 아이스크림인 것이다.
(JAVA로 만든 순수한 객체)
그런데, 직원의 실수로 순수한 하얀 아이스크림을 만들어야 하는데, 아이스크림 원료를 전량 딸기 아이스크림으로 만들어 버린 것이다.
(JAVA언어 외에 다른 기술로 만들어진 객체 예: 다른 프레임워크에서 만들어진 객체)
이러면, 순수한 아이스크림은 고사하고 블루베리 아이스크림도 만들지 못하게된다.
물론, 단순히 토핑만 올라간거면 걷어내도 될수도 있다.
(간단한 코드수정으로 될 수도 있다.)
그런데 아주 빙빙 섞어버리면?
이거를 걷어낼 수도 없고, 걷어낸다 쳐도누가 어느세월에 하겠다고 나서것냐
(프로그래밍으로 치면 수만줄 코드를 수정해야 한다는 것이다.)
이를 방지하기 위해 JAVA로만 만든 객체만 사용한다는 것이다.
순수 JAVA로 만들어진 객체만 썼을 때, 일단 JAVA가 돌아가면 돌아간다는것이다.
좀더 쉽게 말하면, 엔지니어 기본 소양중에 이게 있다.
"이전버전과 호환되는가?"
"다른 기기(목표 기기)에서도 작동하는가?"
이 목적을 달성하기 위해 가장 간단히 해결 할 수 있는게
"가장 기초버젼으로, 가장 구버젼으로 만들면 된다."
와 비슷한 맥락이라고 보면된다.
POJO 프로그래밍이 필요한 이유
- 특정 환경, 기술에 종석적이지 않으니 재사용(다른버젼과 호환)이 가능하고, 확장 가능한 유연한 코드 작성 가능
- 저수준 레벨 기술과 환경에 종속적인 코드를 제거함으로 코드가 깔끔해짐
- 코드가 깔끔해지니 디버깅도 쉬워짐
- 특정 기술, 환경에 얽매이지 않으니 테스트도 쉬워짐
- 객체지향적인 설계를 제한없이 적용할 수 있다.(핵심)
스프링에서는 최대한 POJO프로그래밍을 지향하고 있다.
이 POJO프로그래밍 코드를 작성하기 위해서 스프링에서는 세가지 기술을 지원하는데, 그게 바로 스프링 삼각형 외곽에 있는 IoC/DI , AOP, PSA 이다.
2. IoC (Inversion of Control)
앞에서 잠깐 언급했는데, 프레임워크에서 애플리캐이션 흐름이 프레임워크에게 있고, 라이브러리에서는 개발자에게 있다고 했다.
여기서 애플리케이션 흐름의 주도권이 뒤바뀐 것을 IoC(Inversion of Control)라고한다.
우리가 코드를 작성할때
public static void main(String[] args) {
// 작동시킬 코드
}
위의 코드처럼 main문을 활용하여 작성한다.
직접 우리가 main문을 만들어서 애플리캐이션 흐름을 제어하는것은 - 개발자에게 주도권이 있다.
프레임워크에 주도권이 있는 경우는 어떤 경우인가?
우리가 웹 페이지를 방문할때 일일히 main문을 작성해서 실행시키지 않는다.
이미 동작하는 애플리케이션에 우리가 접속하여 사용하는 "서비스"이기 때문이다.
우리가 사용하는 웹 페이지의 서버는 클라이언트(우리들)의 요청에 따라 직접 실행시켜주고 결과값을 내놓으니 main문이 필요 없고, 주도권이 프레임 워크에 있는 것이다.
아직도 복잡하다면...
강아지와 공 던지기 놀이를 한다고 가정하자.
내가 공을 던지면, 강아지가 물어오는 것이다.
여기서 중요한 것은 내가 강아지한테 "물어와!" 하고 공을 던지는 것은 -> 내가 던진 공, 고로 그 공이 어디로 가는지 얼마나 멀리 갈지 알고 있다.
-> 공에 대한 주도권이 "나"(개발자)에게 있다.
그런데, 내 손을 떠나서 땅에 닿은 순간 강아지가 그 공을 "물어온다."
문제는 강아지가 물어오기 때문에 공을 가져오면서 물웅덩이를 첨벙거릴수도 있고, 뛰어올수도, 천천히 올 수도 있다는 것이다.
이때 공의 속도, 오는거리 등은 내가 정하는게 아니다 -> 강아지가 정하는 것이다!
-> 고로, 공을 물어올때는 강아지(프레임워크)에게 주도권이 있다!
그런데 마냥 강아지가 하는거에 의존해야하나?
"적어도 물웅덩이는 가지 마!"
"빨리와 빨리!"
등의 일부 구체적인 부분은 할 수 있지 않을까?
이 개념을 적용시킨게 바로 DI(Dependency Injection)이다.
3. DI (Dependency Injection)
DI 앞서 설명한 IoC의 개념을 구체화 시킨것으로 보면된다.
Dependency는 '의존하는, 종속되는' 이라는 의미를 가지고 있다.
Injection은 '주입'이라는 의미를 가지고 있다.
둘을 합친 DI는 의존성 주입이라는 의미인데, 일단 의존성부터 짚고 넘어가야 한다.
만약 내가 찬장에 있는 물건을 꺼내고 싶은데, 키가 안닿아서 키 큰 사람을 불러서 꺼낸다면 -> 물건을 꺼내는데 다른사람에 의존하는 것이다.
이와 비슷하게 자바에서는 특정 클래스나 인터페이스의 요소, 객체를 사용하게 되면(상속 등으로), 해당 클래스, 인터페이스의 기능, 객체에 의존하는 것이 된다.
위의 그림을 보면, A클래스가 B클래스의 기능, 객체를 사용한다 -> 클래스 A 는 클래스 B에 의존하고 있다!
바로 코드로 보면 더 헷갈려서 접은글로 넣는다. 나중에 이 글을 읽고 클릭해서 보자

클래스 B를 객체로 만들어 클래스A에서 사용하는 예
MenuService의 기능을 사용하기 위해 MenuController 에서 직접 new 키워드로 MenuService의 객체를 생성함
의존성은 알겠는데, 주입은 뭔가 싶을 수 있다.
앞에서 찬장애 있는 물건을 꺼내달라고 했는데, 부탁을한다면 이럴 것이다.
"저기 위에 있는 물건좀 꺼내줘" -> 내가 다른사람에게 의존하여 특정 행동을 요청하는 것
그런데, 찬장이 생각보다 더 높고, 잘 안보인다면?
"저기 창고에 있는 사다리 가져와서 찬장에 있는 물건좀 꺼내줘" 라고 할 수도 있다.
이때, 우리는 의존성을 "주입"한것이다.
바로 "저기 창고에 있는 사다리 가져와서" 라는 말의 추가로 말이다.
사다리는 객체라서 헷갈린다고?
그러면 사다리를 가져와서 올라가는것은 행위라는 기능이 생기는 거니까, 사다리는 올라가는 행위의 생성자다! 라고 생각하자.
이 역시 먼저 비유를 보고 코드를 보는게 좋아서 접는글로 넣어두었다.

MenuService기능을 사용하기 위해 MenuController의 "생성자"로 MenuService의 객체를 전달받음
이처럼 "생성자"를 통해서 어떤 클래스의 객체를 전달 받는 것을 "의존성 주입"이라고 한다.
"생성자"의 파라미터로 객체를 전달하는것은 "외부에서 객체를 주입한다." 라고 표현한다.
(여기서 외부는 참조되는 클래스, 인터페이스를 의미한다.)
"알아서 하게 두면 되는데 왜 굳이 이것저것 지시함?"
바로 느슨한 결합을 위해서다.
느슨한 결합? 뭔소리냐?
일단 객체지향에서 가장 추구해야 하는것중에 하나가 높은 응집력, 느슨한 결합 이라고한다.
이게 뭔소리냐?
요약하면
"비슷한 일을 하는 기능, 하나의 책임에 포함되는 기능들은 잘 뭉쳐놓는게 좋고, 잘 적용 될수 있게 최대한 유도리 있게 하는것이 좋다." 라는 것이다.
여기서 "유도리있게" 를 위해서는 느슨한 결합이 필요하다는 것이다.
우리가 앞서서 이전에 인터페이스를 사용하면 인터페이스 내의 어떤클래스든 사용 가능하다고 했다.
여기서, 여러개의 클래스를 가진 인터페이스가 있을 때 우리가 그 클래스를 상황에 맞게 그때그때 꺼내오게 하고싶을때가 있을 것이다.
그런데, 만약 인터페이스를 안쓰고 단순히 클래스를 선언해서 객체를 가져온다면 우리가 만든 "하나의" 클래스만 사용 가능하다.
그런데, 인터페이스에 비슷한 클래스를 다 모아놓고, "오버라이딩"을 사용해서 각기 다른 클래스의 같은 이름의 메서드, 객체를 사용 가능한 것이다.
이것도 코드로 바로보면 헷갈려서 접어놧다. 나중에 다 읽고 코드를 보자

오버라이드를 사용해서 클래스 내의 요소를 가져올 수 있다.
좀 쉬운 비유를 하면
우리가 종종 수산시장을 가면 생선을 한무더기 쌓아놓고 판매하는것을 볼 수 있다.
그런데, 그냥 안판다
"큰놈은 한마리 만원, 작은놈은 7천원"
물론 작은놈, 큰놈은 주인장이 정해놓은 기준이고, 사실 우리는 봐도 잘 모른다고 하자.
"큰거 두마리 주시고, 작은거도 한마리 주세요"
라고 한다면 주인장은 어떻게 주는가?
산더미처럼 쌓여있는 애들 중에서 큰놈 두마리, 작은놈 한마리를 꺼내 줄 것이다.
작은놈을 따로 박스에 g단위로 분류해서, 큰놈도 짤짤이로 담아놧다가 꺼내주는게 아니라는 말이다.
여기서 알 수 있듯이, 같은 "종류"(응집력)의 생선을 "조건"에 맞게 (유도리 있게 -> 느슨한 결합) 꺼내주는 것이다.
인터페이스로 치면
인터페이스 = 생선박스
클래스 = 생선 종류
사용하고자 하는 객체 = 생선의 체급
이 되기 때문에, 주인장은 생선박스의 위치와, 그에 맞는 체급의 물고기만 골라주면 되는것이다.
이처럼, 인터페이스를 활용해서 느슨하게 (생선박스만 가리키면) 결합하여 원하는 결과를 여러가지 경우(작은놈, 큰놈으로 주세요)에 대응 가능하다는 것이다
4. AOP (Aspect Oriented Programming)
AOP (Aspect Oriented Programming)을 직역하면 관심 지향 프로그램이라고 한다.
관심지향... 뭐에 관심이 있다는 것인가?
고기를 굽는 경우를 생각해보자
예를 들어 옆집 순이, 나, 앞집 철수랑 고기를 구워먹는다고 하자
순이는 프라이팬이 가장 마이야르가 잘 나오니 최고라고 하고
철수는 숯불로 구워야 정석이라고 하고
나는 그냥 쥰내 쳐먹기 좋기만 하면 장땡이라고 한다고 주장하고 있다고 하자.
굽는 방식은 모두 다른데, 그럼에도 불구하고 공통된 관심사가 존재한다.
바로 "고기를 죤맛탱으로 구워야 한다."이다.
이렇게 같은 일을 다른 방식으로 해도 공통 관심사는 존재한다 이말이다.
이와 마찬가지로 애플리케이션에서 필요한 기능중에서 공통적으로 적용되는 공통기능에 대한 관심과 관련 있는것이 AOP이다.
커피를 주문하는 애플리케이션이 있다고 하자.
커피 메뉴를 등록하거나, 주문하거나, 주문을 변경하는것 모두 로깅, 보안, 트랜잭션(결제 시스템이 있을 수도 있으니)기능을 가지고 있어야 한다.
이 의미는 공통 관심 사항의 기능들이 애플리케이션의 핵심 로직에 전반적으로 두루 사용된다는 것이다.
핵심 관심사항이 공통 관심 사항에서 떵어져 잇는것을 보면 서로 분리되어 있다는 것도 알아 챌 수 있다.
결론적으로 AOP는 애플리케이션의 핵심업무 로직에서 로깅, 보안, 트랜잭션같은 공통 기능 로직들을 분리하는 것이라고 생각하면 이해가 쉽다.
공통된것을 분리해놓고, 모아둠으로 그때그때 가져다 쓰는것이다.
만약 커피를 주문하는 로직, 등록하는 로직, 주문을 변경하는 로직에 로깅, 보안, 트랜잭션을 일일히 구현하여 넣는다면 아주 코드도 길어지고 유지보수할때도 힘들것이다.
고로 AOP로 공통 로직을 모아두어서 중복된 코드를 재사용 하게끔 하면된다.
AOP이거는 어떻게 하냐고?
우리의 킹- 스프링이 AOP기능을 지원해서 알잘딱해준다고 한다.
5. PSA (Portable Service Abstraction)
프로그래밍에서 추상화란 어떤 클래스의 본질적인 특성만을 추출해서 일반화 하는것을 말한다.
클라이언트(사용자, 브라우저)가 추상화된 상위 클래스를 일관되게 바라보며 하위클래스의 기능을 사용하는 것이 일관된 서비스 추상화(PSA)의 기본 개념이다.
위 그림은 클라이언트가 데이터베이스에 연결하기 위해 JdbcConnector를 사용하기 위한 서비스 추상화이다.
JdbcConnector인터페이스 아래에 오버라이딩 된 메서드들을 가진 클래스가 있는것을 볼 수 있는데, 이렇게 인터페이스를 활용하여 간접적으로 연결되어 "느슨한 결합"의 Connection객체를 얻을 수 있다.
클라이언트 입장에서는 어떤 구현체(하위에 위치한 클래스들)를 사용하건, getConnection()메서드를 사용해서 해당 기능을 사용하기 때문에 "일관된 방식으로 해당 서비스르 이용할 수 있다."는 것이다.
코드로 이캐 저캐 설명할수 있겠지만 좀더 헷갈릴것 같아 비유를 들겠다.
편의점에서 담배를 산다고 하자.(한번도 사본적은 없지만 왠지 이럴거 같다.)
우리가 편의점에서 담배를 살 때 담배가 어떤 종류가 잇는지 일일히 보고, 어느 위치에 있는지 알아야 살 수 있는 것이 아니다.
그저 점원분께 "한라산 하나요"라고 하면 그거 준다.
아르바이트생이 바뀌여도, 담배의 배치가 바뀌어도, 우리는 "한라산 하나요" 라고 하면 원하는 담배를 얻을 수 있다는 것이다.
일관된 말로, 서비스의 기능을 접근 할 수 있다는 것이다.
또한, 담배 위치, 아르바이트생의 교체에도 서비스를 받을 수 있다 라는 것이다.
이처럼, 서비스의 기능을 접근하는 방식 자체를 일관되게 유지하면서 기술 자체를 유연하게 사용할 수 있도록 하는 것을 PSA(일관된 서비스 추상화)라고 한다.
PSA가 필요한 주된 이유는 어떤 서비스를 이용하기 위한 접근 방식을 인관된 방식으로 유지하는것으로 애플리케이션이 변경되더라도, 최소한의 변경만으로 요구사항을 반영하기 위함이다.
오늘은 분량이 너무 많았다...
앞으로 매일매일 이러면 못할거 같은디...
으쩌냐...
'백엔드 > 코드스테이츠 수강' 카테고리의 다른 글
[회고]코드스테이츠 수강_10주차_3일차 부스트캠프 2달 경과 (2) | 2022.10.19 |
---|---|
코드스테이츠 수강_9주차_3일차_Spring Framework의 핵심개념 (빈, 스프링 컨테이너, 싱글톤) (1) | 2022.10.13 |
코드스테이츠 수강_8주차_3~4일차_관계형 데이터베이스(데이터베이스 설계) (1) | 2022.10.06 |
코드스테이츠 수강_8주차_2일차_관계형 데이터베이스 (SQL) (1) | 2022.10.05 |
코드스테이츠 수강_8주차_1일차_네트워크(RSET API) (0) | 2022.10.04 |