어제 팀원들이랑 대화하다가 새벽 3시쯤 잠들어서 파워냅 30분했는데 코딩하는 꿈을 꿨습니다...😱 하루종일 코딩만 생각하니 이런 꿈도 꿉니다. 오늘은 주특기 2주차 시작일로 Spring 숙련주차입니다. 강의가 새로 지급되었고 개인과제와 팀과제가 새로 나왔습니다. 다들 월드컵 보러 갈 때 치킨으로 만족하고 강의 듣습니다.🍗🍗🍗
JPA 심화
JPA는 자동으로 Id값을 찾아주지만 (@GeneratedValue 사용) Query는 Select를 사용해 최대값을 넣어서 찾아서 마지막 id값+1해서 넣어줘야 한다
벌써 12월이 되었습니다. 한달이 훌쩍 지나갔네요. 정식으로 부트캠프는 99일이라고 홍보가 되어있지만 사전 스터디 기간까지 합친다면 거진 2달의 기간이 지나갔습니다. 11월에는 자바가 무엇인지 객체지향 프로그램이 무엇인지 처음 배우는 시간이었습니다. 그동한 배우고 구현 해본 것들을 나열하자만 아래와 같습니다.
JWT를 이용해서 Flask python 회원가입, 로그인 기능 구현
자바의 정석 유튜브 동영상 강의 ch 1 ~ 7
프로그래머스 문제 풀이 30개
혼자공부하는 자바 ch 1 ~ 7
깃과 깃허브 사용해서 협업
1주차 미니프로젝트 완성
2주차 알고리즘 test
3주차 Spring 주특기 1주차 강의
객체지향 프로그래밍 과제
게시판 구현 (CRUD 기능 구현: 등록, 조회, 수정, 삭제)
Postman 사용
JPA 기초
HTTP method (PUT, POST, DELETE 등)
API 명세서 작성
API 만들기
생각보다 한 게 많지만 없습니다.
개발자가 되기 위한 단계로 보면 이제 튜토리얼의 튜토리얼을 뗀 수준입니다.
자바도 그렇고 이제 갓 배우기 시작해서 뭐든 다 새롭고 매일 엄청난 양의 새로운 정보를 습득하다 보니 혼란할 때도 많습니다. 어제 튜터님께 자바 공부를 하고 있다고 말씀드리니까 자바 백엔드 개발자 공부 로드맵을 이야기 해주셨습니다.
자원 전체를 교체하는 것이기에, Client는 해당 자원 상태를 모두 알고 있다고 가정되어야 함 즉, Payload만으로 자원 전체 상태를 표시할 수 있어야 함
멱등성 가짐
2)PATCH
부분 수정(자원 부분 교체)
수정 시 수정할 필드만 필요
별도의 DTO 필요
멱등성 가질 수 도, 안 가질 수도 있음
멱등성(idemoitent)
연산을 여러번 적용해도, 결과가 달라지지 않는 성질
연산을 여러번 반복해도, 한번만 수행된것과 같은 성질
동일한 요청을 한번 보내는 것과 여러번 연속 보내는 것이 같은 효과를 가지고, 서버 상태도 동일할 때, HTTP Method가 멱등성을 가졌다고 말함
ex) f(f(x)) = f(x)
HTTP란?
HyperText Transfer Protocol로 프로토콜, 즉 인터넷 통신 데이터 교환의 기준이 되는 약속이다. Stateless(무상태성)와 Connectionless(비연결성)이 특징으로 요청(request)을 주고 받을 때만 연결이 유지되고 응답(response)하고 나서 서버와 연결을 끊는다.
HTTP 메소드
클라이언트가 웹 서버에 사용자 요청의 목적 또는 종류를 알리는 수단으로 GET, POST, PUT, PATCH, DELETE 가 주로 쓰임
구성요소
Method (호출/요청 방식)
GET: 이름 그대로 어떤 리소스를 얻을 때 사용; 브라우저의 주소창에 URL을 입력하면 GET 메서드를 사용해서 서버에 요청을 보냄
POST: 웹 서버에 데이터를 게시할 때 사용 (ex. 회원가입, 게시글 작성, 댓글 작성)
Header (추가 데이터. 메타 데이터)
브라우저가 어떤 페이지를 원하는지
요청 받은 페이지를 찾았는지
요청 받은 데이터를 성공적으로 찾았는지
어떤 형식으로 데이터를 보낼지
다양한 의사 표현을 위한 데이터를 모두 Header 필드에 넣고 주고 받음
위에서 설명 된 메서드도 헤더에 포함되어 서버로 보냄
Payload (데이터. 실제 데이터)
서버가 응답을 보낼 때에는 항상 Payload 보내기 가능
클라이언트(브라우저)가 요청을 할 때에도 Payload를 보낼 수 있음
"GET method를 제외하곤 모두 Payload를 보낼 수 있다" 는게 HTTP에서의 약속
Spring에서 선언적(@Transactional)으로 Transaction 처리 제공 (개발할 때 Transaction 범위를 @Transactional 어노테이션을 사용해 손쉽게 정의 가능)
class level 또는 method level에 @Transactional 어노테이션이 붙어있는 Service 클래스는 Transaction 관련 코드(트랜잭션 시작, 커밋 또는 롤백, 트랜잭션 종료)가 추가된 Proxy 객체를 Bean으로 등록해 Transaction 기능을 수행
기본적으로 읽기전용 인 @Transactional(readOnly=true)는 아무런 @Transactional 설정을 하지 않은 메서드에 적용
스프링에서 의존성 주입이란 무엇인가?
외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴이다. 인터페이스를 사이에 두고 클래스 레벨에서 의존관계가 고정되지 않도록 한 후에 런타입 시에 관계를 동적으로 주입하여 유연성을 높이고 결합도를 낮출 수 있게 하는 것을 의미한다.
예시-문제점) 아래의 코드는 두가지의 문제를 가지고 있다.
**문제점 1. 두 클래스의 결합도가 높음**
→ 만약 다른 타이어를 사용하고자 할 때 Car() 클래스의 직접 수정이 필요하다.→ 구현클래스를 직접 수정해야한다.
문제점 2. 객체들 사이의 관계가 아닌 클래스같의 관계가 맺어져 있다.
public class Car() { private Tire kumhoTire; public Car() { this.kumhoTire = new kumhoTire(); } }
예시-DI수행) 그럼 스프링에서의 DI를 수행해보자.
먼저 Tire 인터페이스를 생성하고 그것을 구현하는 kumhoTire 구현 클래스를 만든다.
public interface Tire { } public class kumhoTire implements Tire { }
그리고 Car의 생성자를 만들고 인자값으로 Tire 객체를 받도록 만든다. 이렇게 함으로써 Car에서 kumhoTire 구체 클래스의 의존성을 제거한다.
public class Car { private Tire tire; private Car(Tire tire) { this.tire = tire; } }
스프링은 애플리케이션 실행 시점에서 필요한 객체(bean)을 DI 컨테이너에서 생성한다. 그리고 의존성이 있는 두 객체를 연결하기 위해 아래와 같이 한 객체를 다른 객체로 주입시킨다.
public class BeanFactory { // Bean의 생성 Tire kumhoTire = new KumhoTire(); // 의존성 주입 Car car = new Car(kumhoTire); }
이러한 개념은 동시에 **제어의 역전(Inversion of Control, IOC)**라고 불린다. 왜냐하면, 어떠한 객체를 사용할지에 대한 책임을 프레임워크에 넘겼기 때문이다. 동시에 자신은 수동적으로 주입받는 객체를 사용하기 때문이다.
스프링에서 IOC (Inversion Of Control)란?
IOC는 제어의 역전, 즉 제어의 흐름을 바꾸는 것이다.
Java 프로그램에서는 각 객체들이 프로그램의 흐름을 결정하고 객체를 직접 생성하여 메소드 호출하는 방식의 작업을 했다.
사용자가 모든 작업을 제어하는 구조이다. ( A객체에서 B 객체에 있는 메소드를 사용하고 싶을 때 B 객체를 직접 A 객체 내에서 생성하고 메소드를 호출)
IOC가 적용된 경우에는 객체의 생성을 특별한 관리 위임 주체에게 맡긴다.
즉, 사용자가 객체를 직접 생성하지 않고 객체의 생성 및 소멸과 같은 생명주기에 관한 제어권을 다른 주체에게 넘긴다.
클래스 내부의 객체 생성 -> 의존성 객체의 메소드 호출이 아니라, 스프링에게 제어를 위임하여 스프링이 만든 객체를 주입 -> 의존성 객체의 메소드 호출 구조이다.
모든 의존성 객체를 스프링이 실행될 때 만들어 주고 필요한 곳에 주입한다.
Spring이 모든 의존성 객체를 Spring이 실행 될 때 다 만들어 주고 필요한 곳에 주입 시켜줌으로써 Bean들은 싱글톤 패턴의 특징을 갖습니다.
각 계층을 전문적으로 다룬다는 목적이 있다. 레이어드 아키텍쳐는 구성요소들이 수평적인 레이어로 조직화되어 있는 다층 구조이며, 모든 구성요소가 연결되어 있지만 독립적이라 말할 수 있다.
Presentation Layer
클라이언트로 응답을 다시 보내는 역할을 담당하는 모든 클래스가 포함된다.
Business Layer
시스템을 구현해야하는 로직들을 해당 레이어에서 구현하게 된다. 접근성 , 보안 , 인증 , 유효성 검사 와 같은 로직들이 해당 계층에서 발생한다.
Persistence Layer
데이터베이스에서 데이터를 저장, 수정, 읽는 등 데이터베이스와 관련된 로직을 구현한다. DAO(Data Access Object)presentation, ORM(Object Relational Mappings) 등을 포함한다.
Database Layer
데이터 베이스가 저장되는 레이어
특징
레이어드 아키텍쳐는 하위 계층에만 의존한다. 위 그림 계층 구조에서 비즈니스 레이어는 프레젠테이션 레이어로부터 독립적이고 , 퍼시스턴스 레이어에는 의존적이다. 그래서 하위계층의 변경에 있어서는 상위계층은 신경쓰지 않아도 되는 장점이 있다. 이 특징으로 각 계층의 역할들이 명확해지며, 개발과 테스트가 용이해진다.
Entity와 DTO란?
Entity
실제 DB 테이블과 매핑되는 핵심 클래스로, DB 테이블에 존재하는 컬럼들을 필드로 가지는 객체
DTO (Data Transfer Object)
계층간 데이터 교환이 이루어 질 수 있도록 하는 객체
Entity와 DTO를 분리하는 이유 ⇒ 관심사 분리!!
관심사의 분리 : Entity와 DTO를 분리해야 하는 가장 근본적인 이유는 관심사가 서로 다르기 때문! (*관심사의 분리(separation of concerns, SoC)는 소프트웨어 분야의 오래된 원칙 중 하나로써, 서로 다른 관심사들을 분리하여 변경 가능성을 최소화하고, 유연하며 확장가능한 클린 아키텍처를 구축하도록 도와줌)
Entity의 값이 변하면 Repository 클래스의 Entity Manager의 flush가 호출될 때 DB에 값이 반영되고, 이는 다른 로직들에도 영향 미친다. View와 통신 하면서 필연적으로 데이터의 변경이 많은 DTO클래스를 분리해주어야 한다
도메인 설계가 아무리 잘 되있다 해도 Getter만을 이용해서 원하는 데이터를 표시하기 어려운 경우가 발생할 수 있는데, 이 경우에 Entity와 DTO가 분리되어 있지 않다면 Entity안에 Presentation을 위한 필드나 로직이 추가되게 되어 객체 설계를 망가뜨리게 된다. 때문에 이런 경우에는 분리한 DTO에 Presentation로직 정도를 추가해서 사용하고, Entity에는 추가하지 않아서 도메인 모델링을 깨뜨리지 않는다.
Singleton Pattern이란?
싱글톤 패턴이란 인스턴스를 1개로 제한하며 어디서든 접근 할 수 있도록하는 객체 생성 디자인 패턴이다. 싱글톤 클래스는 본인을 호출하는 static 메소드를 가지고 있으며, 이 메소드를 호출하면 본인을 반환하도록 설계된다.
객체 지향 프로그래밍에서는 모든 객체들은 라이프 사이클(객체가 생성되고 GC에 의해 삭제되기 까지)을 가지고 있다. 객체를 여러개 만들지 않고 1개만 만들고 공유하고 싶을 때 사용할 수 있는 패턴이 싱글톤 패턴이다. (ex. DB의 Transaction을 관리하는 클래스)
Singleton Pattern의 문제점
싱글톤 패턴을 구현하는 코드 자체가 많이 들어간다.
의존 관계 클라이언트가 추상화 클래스에 의존하는 게 아니라 구체 클래스에 의존하게 되면서 객체 지향 설계 5원징인 SOILD 중 DLP(의존관계 역전 원칙)를 위한하고 OCP(개방-폐쇄 원칙)을 위반할 가능성이 높다.
내부 속성을 변경하고 초기화하는게 어렵고 private 생성자로 자식 클래스를 만들기가 어렵다.
Spring에서의 Singleton Pattern
스프링에서는 클래스 자체에 의해서 객체를 생성하지 않고 스프링 컨테이너에 의해 구현이 된다.
스프링 컨테이너는 싱글톤 패턴의 문제를 해결하면서 싱글톤 패턴을 적용하지 않아도 객체의 인스턴스를 싱글톤으로 관리한다.
스프링의 싱글톤 레지스트리 - 싱글톤 패턴의 단점을 보완하기 위해 나온것 - 스프링 컨테이너가 싱글톤 컨테이너 역할을 하고 싱글톤 객체를 생성하고 관리하는 기능
스프링 컨테이너 기능 덕분에 싱글톤 패턴의 단점을 해결하면서 싱글톤으로 유지할 수 있다.
결국 스프링 Bean이 싱글톤으로 관리되는 Bean이다
Factory Method Pattern
객체 생성 처리를 서브 클래스로 분리하여 처리하도록 하는 캡슐화 패턴이다. 즉, 객체의 생성 코드를 별도의 클레스/메소드로 분리함으로써 객체 생성의 변화에 대비하여 유용하게 사용할 수 있다.
Factory Method Pattern의 장단점
장점
비슷한 성격의 객체를 인터페이스를 통해 하나로 관리할 수 있다. (코드가 간결해진다.)
비슷한 유형의 객체가 생성되어도 implement를 통해 쉽게 추가할 수 있다.
단점
제품 클래스가 바뀔 때 마다 새로운 서브 클래스를 작성해야 한다.
클래스가 많아지기 때문에 클래스 계층도 커질 수 있다.
Factory Method Pattern의 구조
Product는 인터페이스를 선언한다. 인터페이스는 생성자와 자식 클래스들이 생성할 수 있는 모든 객체에 공통이다.
Concrete Prodect들은 제품 인터페이스의 다양한 구현들이다.
Creator 클래스는 새로운 제품 객체들을 반환하는 Factory Method를 선언한다. 이 때 Factory Method의 반환 유형이 Product 인터페이스와 일치해야한다.
ConcreteCreator들은 기초 Factory Method를 오버라이드(재정의)하여 다른 유형의 Product를 반환하게 하도록 한다.
Factory Method는 항상 새로운 인스턴스를 생성해야 할 필요가 없다. 기존 객체들을 캐시, 객체 풀 또는 다른 소스로부터 반환할 수 있다.
Proxy Pattern
어떤 객체에 대한 접근을 제어하기 위한 용도로, 실제 객체의 메소드를 호출하면 그 호출을 중간에 가로채는 패턴이다.
즉, 제어 흐름을 조정하기 위해 중간에 대리자를 두는 패턴
스프링에서는 AOP를 통해서 이런 공통적인 기능들을 별도로 분리해 필요한 곳에서 재사용이 가능
AOP(Aspect Oriented Programming)이란?
관점 지향 프로그래밍 - 공통 기능과 핵심 기능을 분리시켜 공통 기능을 계속 재활용하여 사용하는 방식 (main기능이 아닌 기능(Logging/Transcation)을 묶음으로 하여 Main 기능을 잘 구성할 수 있게 해주는 방식)
Proxy Pattern의 구조
Client가 해당 함수를 직접 호출하는 것이 아닌 Proxy를 호출한다.
Proxy Class에서 실제 Class를 호출한다.( Proxy Class는 실제로 호출할 Class를 이미 가지고 있다.
실제 Class에서 반환 받은 값을 Client에게 반환한다.
Proxy Pattern의 장단점
장점
사이즈가 큰 객체가 로딩되기 전에도 proxy를 통해 참조를 할 수 있다.(이미지, 동영상 등의 로딩에서 반응성 혹은 성능 향상 - 가상 프록시)
실제 객체의 public, protected 메소드들을 숨기고 인터페이스를 통해 노출시킬 수 있다.( 안정성 - 보호 프록시 )
로컬에 있지 않고 떨어져 있는 객체를 사용할 수 있다. (RMI, EJB 의 분산처리 등 - 원격 프록시)
원래 객체의 접근에 대해서 사전처리를 할 수 있다. (객체의 레퍼런스 카운트 처리 - 스마트 프록시 )
원본 객체의 참조만 필요할 때는 원본 객체를 사용하다가, 최대한 늦게 복사가 필요한 시점에 원본 객체를 복사하여 사용한다. (Concurrent package의 CopyInWriteArrayList - 변형된 가상 프록시)
단점
객체를 생성할 때 한단계를 거치게 되므로, 빈번한 객체 생성이 필요한 경우 성능이 저하될 수있다.
프록시 안에서 실제 객체 생성을 위해서 thread가 생성되고 동기화가 구현되야하는 경우 서능이 저하되고 로직이 난해해질 수 있다.
interface 인터페이스이름 {
public static final 타입 상수이름 = 값; //상수
public abstract 메서드이름(매개변수목록); //추상메서드
}
//인터페이스의 모든 메소드는 public이고 final이고 abstract 여서 생략 가능
인터페이스의 상속
인터페이스의 조상은 인터페이스만 가능(Object가 최고 조상이 아님)
다중 상속이 가능 (추상메서드는 충돌해도 문제 없음)
interface Fightable extends Movable, Attackable {}
interface Movable {
/** 지정된 위치(x,y)로 이동하는 기능의 메서드 */
void move(int x, int y);
}
interface Attackable {
/** 지정된 대상(u)을 공격하는 기능의 메서드 */
void attack(Unit u);
}
인터페이스의 구현
인터페이스에 정의된 추상 메서드를 완성하는 것
class 클래스이름 implements인터페이스이름 { // 인터페이스에 정의된 추상메서드를 모두 구현해야 한다 }
구현한다는 의미의 키워드 'implements'를 사용함
class Fighter implements Fightable {
public void move(int x, int y) { /*내용 생략 */}
public void attack(Unit u) { /*내용 생략 */} // 몸통 완성
}
//Fighter 클래스는 Fightable 인터페이스를 구현했다.
추상클래스 완성과 동일; 키워드만 다름
일부만 구현하는 경우, 클래스 앞에 abstract를 붙어야 함
인터페이스란?
추상 메서드의 집합
인터페이스의 구현이란?
인터페이스의 추상메서드 몸통{} 만들기(미완성 설계도 완성하기)
추상 클래스와 인터페이스의 공통점은?
추상 메서드를 가지고 있다 (미완성 설계도)
추상 클래스와 인터페이스의 차이점은?
인터페이스는 iv를 가질 수 없다
인터페이스를 이용한 다형성
인터페이스도 구현클래스의 부모
인터페이스 타입 매개변수는 인터페이스 구현한 클래스의 객체만 가능
인터페이스를 메서드를 리턴타입으로 지정할 수 있다
abstract class Unit2 {
int x, y;
abstract void move(int x, int y);
void stop() {
System.out.println("멈춥니다.");
}
}
interface Fightable { //인터페이스의 모든 메서드는 public abstract. 예외 없이
void move (int x, int y); // public abstract가 생략됨
void attack(Fightable f); // public abstract가 생략됨
}
class Fighter extends Unit2 implements Fightable{
//오버라이딩 규칙: 조상(public) 보다 접근제어자가 좁으면 안된다.
public void move (int x, int y) {
System.out.println("["+x+","+y+"]로 이동");
}
public void attack(Fightable f){
System.out.println(f+"를 공격");
}
}
public class FighterTest {
public static void main(String[] args) {
// Fighter f = new Fighter();
Unit2 u = new Fighter(); //Unit2에는 attack()이 없어서 호출불가
Fightable f = new Fighter();
u.move(100, 200);
// u.attack(new Fighter()); //Unit2에는 attack()이 없어서 호출불가
u.stop();
f.move(100, 200);
f.attack(new Fighter());
// f.stop(); //Fightable에는 stop()이 없어서 호출불가
}
}
인터페이스의 장점
두 대상(객체) 간의 '연결, 대화, 소통'을 돕는 '중간 역할'을 한다
GUI - Graphic User Interface
선언(설계, 껍데기)와 구현(알맹이)을 분리시킬수 있게 함
변경에 유리하고 유연한 코드가 됨
인터페이스 덕분에 B가 변경되어도 A는 안바꿀 수 있게 됨 -> 느슨한 결합
의존도가 낮아짐
개발 시간 단축 가능
변경에 유리한 유연한 설계 가능
표준화가 가능
서로 관계없는 클래스들을 관계를 맺어줄 수 있음
다중상속 가능
class A{
public void method(I i) { //인터페이스 I를 구현한 넘들만 들어와라
i.method();
}
}
// B클래스의 선언과 구현을 분
interface I {
public void method();
}
class B implements I{
public void method(){
System.out.println("B클래스의 메서드");
}
}
class C implements I{
public void method(){
System.out.println("C클래스의 메서드");
}
}
public class InterfaceTest {
public static void main(String[] args) {
A a = new A();
a.method(new C()); // A가 B를 사용(의존)
}
}
abstract class Player { //추상 클래스
abstract void play(int pos); //추상 메서드
abstract void stop(); //추상 메서드 (선언부만 있고 구현부{}가 없는 메서드)
}
//추상 클래스는 상속을 통해 완성해야 객체 생성가능
class AudioPlayer extends Player {
@Override
void play(int pos) {
System.out.println(pos+"위치부터 play 합니다");
}
@Override
void stop() {
System.out.println("재생을 멈춥니다.");
}
}
public class PlayerTest {
public static void main(String[] args) {
// Player p = new Player(); // 추상 클래스의 객체를 생성
// AudioPlayer ap = new AudioPlayer();
Player ap = new AudioPlayer(); //다형성
ap.play(100);
ap.stop();
}
}
추상클래스의 작성
- 여러 클래스에 공통적으로 사용될 수 있는 추상클래스를 바로 작성하거나 기존 클래스의 공통 부분을 뽑아서 추상클래스를 만든다
public class Ex7_10 {
public static void main(String[] args) {
// Unit[] group = new Unit[]{new Marine(), new Tank(), new Dropship()};
Unit[] group = new Unit[3];
group[0] = new Marine();
group[1] = new Tank();
group[2] = new Dropship();
for(int i = 0; i < group.length; ++i)
group[i].move(100, 200);
}
}
abstract class Unit {
int x, y;
abstract void move(int x, int y);
void stop() {/*현재 위치에 정지*/}
}
class Marine extends Unit { //보병
void move(int x, int y) {
System.out.println("Marine[x="+x+",y="+y+"]");
}
void stimPack() {/*스팀팩을 사용한다. */}
}
class Tank extends Unit { //탱크
@Override
void move(int x, int y) {
System.out.println("Tank[x="+x+",y="+y+"]");
}
void changeMod() { /*공격모드를 변환한다.*/}
}
class Dropship extends Unit { //탱크
@Override
void move(int x, int y) {
System.out.println("Dropship[x="+x+",y="+y+"]");
}
void change() { /*공격모드를 변환한다.*/}
}