Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- islowercase()
- ineer join
- StringBuilder
- 유클리드호제법
- 프로그래머스 레벨1
- 베주계수
- while과 two-pointer
- 스프링환경설정
- git 컨벤션
- string
- 모던자바
- string과 stringbuilder
- 최소공배수
- replaceAll()
- 자바 유클리드
- 최대공약수와 최소공배수
- 스프링뼈대
- 자바 최소공배수
- stringbuilder의 reverse()
- toLowerCase()
- isuppercase()
- 자바 최대공약수
- 동일성과 동등성
- Git사용법
- addDoc
- 최대공약수
- sql 데이터형 변환
- 래퍼타입
- 자바 스트링
- 스프링
Archives
- Today
- Total
주노 님의 블로그
20240816 내배캠 25일차 TIL 본문
본캠프 25일차 내용 간단요약
- 09:00 ~ 10:00 : 코드카타
- 10:00 ~ 10:10 : 팀 회의
- 10:20 ~ 11:00 : 과제마무리
- 11:00 ~ 12:00 : 강의
2주차 3강 ~ 2주차 4강 - 12:00 ~ 13:00 : 점심시간
- 13:00 ~ 18:00 : 강의
2주차 5강 ~ 2주차 10강 - 18:00 ~ 19:00 : 저녁시간
- 19:00 ~ 21:00 : TIL정리
오늘 해야할 일 ✔️ 🔺 ❌
🔺2주차 강의 듣기
🔺복습하기
🔺피드백 내용으로 개선하기
오늘의 요약
(대충 키보드를 내리치며 리듬을 타는 나)
코드카타
더보기
문제 분석
바로 앞번호나 뒷번호의 학생에게만 빌려줄수 있다
여벌의 체육복이 있는 학생만 빌려줄 수 있다
여벌의 체육복이 있는 학생도 도둑을 맞을 수 있다
class Solution {
public int solution(int n, int[] lost, int[] reserve) {
int answer = 0;
int [] result = new int[n];
for(int i = 0 ; i <reserve.length;i++)
{
result[reserve[i]-1]++;
}
for(int i = 0; i<lost.length; i++)
{
result[lost[i]-1]--;
}
for(int i = 0; i < n; i++)
{
if(result[i]==1)
{
if(i!=0 && result[i-1]==-1)
{
result[i-1]++;
result[i]--;
}
else if(i!=n-1 && result[i+1]==-1)
{
result[i+1]++;
result[i]--;
}
}
}
for(int i = 0 ; i <n ; i++)
{
if(result[i]==0 || result[i]==1)
{
answer++;
}
}
return answer;
}
}
요롷게 풀어버렸다
i!=0과 i!=n-1을 해야지 범위를 넘지않는다
그리고 여벌이있는 학생과 옷을 가지고있는학생둘다 카운트해야하니
result[i]==0 || result[i]==1을 해서 카운트를 해야한다!
set을 사용하면 더 효율적으로 계산할 수 있다던데
아직은 코테문제에 컬렉션을 (list빼고) 자유자제로 못쓴다 이말이지..
주말에 한번 코테 공부좀 해야겠다..
강의 - IOC와 BEAN
더보기
- 좋은 코드란?
새로운 기능을 추가하더라도 구조의 변경이 없어야한다
의존성을 최소화해야함
중복을 제거
코드를 처음 보는사람도 쉽게 해석할 수 있어야함 - 의존성이란?
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는 chicken과 강하게 결합되어있다
왜냐하면 consumer가 chicken이아닌 pizza를 먹고싶다면 내부 메서드를 수정해야하기 때문이다
이를 강하게 결합했다고 하며, 좋지않은 구현이다
결합을 약하게 하는 방법은??
자바의 인터페이스를 사용한다
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("피자를 먹는다."); } }
인터페이스를 사용하여 eat메서드를 만들면 customer에서는 코드의 수정이 많이 일어나지 않는다
이런 상태를 약한 결합이라고 한다 - 주입이란?
필요로하는 객체를 해당 객체에 전달하는것 이다.
필드에 직접 주입
필드에 직접 주입하는 방법이 있고public class Consumer { Food food; void eat() { this.food.eat(); } public static void main(String[] args) { Consumer consumer = new Consumer(); consumer.food = new Chicken(); consumer.eat(); consumer.food = new Pizza(); consumer.eat(); } }
메서드를 통한 주입
public void setFood(Food food) { this.food = food; }
메서드를 통한 주입이 있다.
생성자를 통한 주입
public Consumer(Food food) { this.food = food; }
consumer 생성자를 불러올때
Food 객체도 불러온다는 뜻이다 - 제어의 역전이란?
customer가 food를 호출하기떄문에 새로운 food를 만들면 코드를 변경했어야했다
그때의 제어의 흐름은 customer > food 였다.
그것을 해결하기위해 food를 customer에 전달하는 방식으로 customer은 food가 어떤것이 되어도 먹을수 있다.
제어의 흐름이 food > consumer로 역전이 되었다고 한다.
IoC & DI 적용하기
- 현재 프로젝트의 문제점?
service에서 보면 위 코드가 중복되는것을 알 수 있다MemoRepository memoRepository = new MemoRepository(jdbcTemplate);
private final JdbcTemplate jdbcTemplate; public MemoService(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; }
중복되는 코드를 줄이고자 위코드에서
private final MemoRepository memoRepository; public MemoService(JdbcTemplate jdbcTemplate) { this.memoRepository = new MemoRepository(jdbcTemplate); }
위처럼 생성자에서 주입을 한 후 정리를한다.
controller도 위와같이 수정한다.
위처럼 구현을 하게되면 메모서비스가 메모레파지토리를 만들고있다
제어의 흐름 : memocontroller > memoservice > memorepository로 가고 있다.
강하게 결합이되어있다!
약하게 결합으로 바꿔보자
public MemoController(MemoService memoService) { this.memoService = memoService; }
private final JdbcTemplate jdbcTemplate; public MemoRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; }
직접적으로 jdbctemplate를 사용하는 repository만 남겨놓고, controller와 service는 위 처럼 없애도 된다. 미리 만들어진 객체를 참조한다는 느낌으로.
다만 의존성 주입은 객체의 생성이 우선되어야한다
위 객체의 생성은 누가해주지?? - BEAN
일반 클래스를 스프링이 관리하는 bean 객체로 만들기 위한 어노테이션 @Component을 사용한다
스프링이 run될때 ioc컨테이너가 해당 클래스를 bean객체로 만든다.
어떻게 위 어노테이션만 적용하면 등록해줄까?
@SpringBootApllication은 Component 어노테이션을 스캔해주는 어노테이션을 가지고있다.
따라서 @Component 어노테이션을 달고있는 모든 클래스는 Bean객체로 등록해준다.
강한 결합을 약한 결합으로 바꾸기위해 위처럼 선언했다.
하지만 Controller에서 위와 같은 오류가 뜨고 있었다.
외부에서 의존성을 주입한다면 스프링에서 어노테이션을 적용해줘야한다.
@Component public class MemoService
memoService에 Component 어노테이션을 적용한다
그럼 controller에서는 오류가 사라지는 것을 볼 수 있다.
MemoService가 bean객체로 등록이 되어서 문제가 해결된것을 볼 수 있다.
그럼 또 Service가 문제가 생기는데 Repository도 Bean 객체로 등록하기 위해서 어노테이션을 위와같이 적용한다
bean으로 등록되면 끝나나?
아니다. 주입받는 생성자에다가 AutoWired를 사용해야한다, 단 클래스의 생성자가 하나일때만 생략이 가능하고,
생성자가 많을때는 @AutoWired를 사용해야한다.
위에서 의존성 주입은 객체의 생성이 우선되어야 한다고했다
하지만, 객체를 생성하지 않은 상태에서 실행 초기시점에 어떻게 만들어 질 수 있을까?
우선 스프링을 시작하면
@ SpringBootApllication은 실행되면서, @Component를 찾아서 Bean으로 등록을한다.
찾아진 Component의 @AutoWired를 찾아서 의존성 주입이 필요한 생성자에 Bean객체를 등록해준다.
따라서 우선 Bean으로 등록이 되어야지 객체를 주입할수 있는것이다.
@Component가 없으면 @AutoWired를 사용할 수 없다는뜻!
- 3Layer Annotation
@Controller, @RestController
@Service
@Repository
라는 어노테이션이 있다
위 어노테이션이 있어도 자동으로 빈을 등록해주며, 스프링에서 관리를 해준다
어떻게? -
Controller 어노테이션을 보자
보면 Controller 어노테이션 위에 @Component 어노테이션이 부착되어 있는것을 볼 수 있다.
그래서 가능하다 ㅇㅇ.
따라서
@Controller, @RestController
@Service
@Repository
를 사용하여 어노테이션을 하면 @Component 어노테이션을 한것과 같다.
- 3Layer Annotation
강의 - JPA
더보기
- 객체를 직접 DB에 저장해야한다면?
JDBC를 만들며 DAO를 만듬.
새로운 요구사항을 추가해야함
테이블에 새로운 필드를 추가해야함
등록 조회 수정 연관된 객체를 모두 수정해야하는 문제가 생김.
SQL에 의존적인 개발(SQL이 변경되면 모든 코드가 변경될수밖에없음)
개발은 요구사항이 추가될때마다 객체를 mapping하는것과, SQL을 수정하는데 할애해야함 - RDB와 객체지향의 패러다임 불일치
RDB는 객체지향의 상속, 연관관계 객체 그래프 탐색등의 기능이 없음
또한 객체지향은 RDB처럼 외래키를 양방향 참조하는 기능이 없음.
그 간극을 개발자가 매꿔야함
JPA는 위의 문제를 대신 해결해줌 - 객체 그래프 탐색?
객체지향에서는 연결된 객체를 자유롭게 탐색이 가능해야함
SQL의 의존적인 개발에서는 DAO를 통해 확인해야함
객체지향에 가까워질수록 SQL과의 간극은 커짐 - ORM
객체와 DB의 관계를 편리하게 매핑해주는 스프링 도구이다 - JPA
자바 진영의 ORM 기술 표준임
JPA는 애플리케이션과 JDBC 사이에서 동작되고있으며
객체와 관계형 데이터베이스를 매핑해줌
위의 패러다임 불일치 문제를 대신 해결해줌. - JPA의 이점
생산성 : SQL 작업 생략
유지보수 : 테이블 구조나 요구사항의 추가 변경에 유연하게 대체, 객체지향 가능
패러다임 불일치 해결 : 상속, 연관관계, 객체 그래프탐색등이 가능함.
데이터 접근 추상화
벤더 독립성
각각의 데이터베이스 개발 툴이 있음
그 개발 툴마다 사용하는 언어(방언)이 다름.
만약 강하게 결합된 언어라면 다른 DB로 교체했을시, 문제가 생김.
JPA는 각각의 DB언어(방언)을 구현하여 MYSQL에서 작업하다 ORACLE로 바꿔야할때의 오류가 없음. - 하이버네이트
성숙한 객체지향 언어에는 ORM 프레임워크가 있음
자바 진영에도 EJB라는 기술이 있었지만 복잡하고 성숙도도 떨어짐
개빈 킹이 개발한 하이버네이트가 인기가 되어, 자바 진영의 ORM 표준으로 재정되었음. - 영속성 컨텍스트
영속성(persistence)이란? 지속성
객체의 상태를 지속적으로 저장할 수 있는 성질
Entity 객체를 효율적이고 쉽게 관리하기위해 만들어진 공간이라고 생각하면 좋다!
영속성 컨텍스트란 엔티티의 상태를 추적하고 관찰하는 공간이다.
영속성 컨텍스트에 접근하기위해 EntityManager이 필요하다
EntityManager은 영속성 컨텍스트의 Entity를 관리한다
EntityManager은 EntityManagerFactory를 통해 생성한다 - EntityManagerFactory
EntityManagerFactory는 db에 하나만 생성되어 애플리케이션이 동작하는동안 사용된다
비용이 많이드는 객체이기때문에 꼭 하나만 생성되는것을 권장한다.
DB에 대한 정보를 등록해줘야하는데 PERSISTENCE.XML에 정의되어있다.
<persistence-unit name="memo">
persistence.xml 파일을 확인해보면 persistence unit에 memo 라고 되어있다
emf = Persistence.createEntityManagerFactory("memo"); em = emf.createEntityManager();
Persistence클래스의 createEntityManagerFactory의 unit의 name을 매개변수로받아
createEntityManager()를 통해 entityManager를 생성한다.
entiyManager은 스레드 간의 공유가 되지 않으며, 트랜잭션단위로 생성 및 종료가된다. - JPA의 트랜잭션
트랜잭션이란? DB의 무결성과 정합성을 유지하기위한 하나의 논리적 개념이다
트랜잭션 안의 모든 SQL문이 정상적으로 실행되어야 DB에 반영이되지만.
SQL문이 하나라도 실패한다면 되돌린다
위와같이 INSERT문을 날려도 DB에는 업로드가 되지않는다.
커밋을 하는 순간 (트랜잭션이 실행되는 도중에 문제가 없을 경우에) 업로드가 되는것을 볼 수 있다.
기본키 조건에 위배되는 중복되는 id를 삽입하였을 경우에는
위와같이 오류가뜬다
3번라인과 4번라인은 정상적으로 실행됐지만,
5번라인에서 멈춰서게 된것이다.
jpa도 위와 유사하게 작동한다.
변경이 발생한 엔티티의 객체들의 정보는 jpa의 영속성 컨텍스트에 저장되고, jpa는 이러한 변경사항을 커밋될때까지 지연시켜둔다.
위처럼 트랜잭션을 사용하지않고 db수정 관련 작업을 수행한다면
트랜잭션이 없다는 경고문이 뜨게된다.!
select는 예외
Test @DisplayName("EntityTransaction 성공 테스트") void test1() { EntityTransaction et = em.getTransaction(); // EntityManager 에서 EntityTransaction 을 가져옵니다. et.begin(); // 트랜잭션을 시작합니다. try { // DB 작업을 수행합니다. Memo memo = new Memo(); // 저장할 Entity 객체를 생성합니다. memo.setId(1L); // 식별자 값을 넣어줍니다. memo.setUsername("Robbie"); memo.setContents("영속성 컨텍스트와 트랜잭션 이해하기"); em.persist(memo); // EntityManager 사용하여 memo 객체를 영속성 컨텍스트에 저장합니다. et.commit(); // 오류가 발생하지 않고 정상적으로 수행되었다면 commit 을 호출합니다. // commit 이 호출되면서 DB 에 수행한 DB 작업들이 반영됩니다. } catch (Exception ex) { ex.printStackTrace(); et.rollback(); // DB 작업 중 오류 발생 시 rollback 을 호출합니다. } finally { em.close(); // 사용한 EntityManager 를 종료합니다. } emf.close(); // 사용한 EntityManagerFactory 를 종료합니다. }
위 코드는 jpa환경에서 트랜잭션을 시작하는 예시이며
EntityTreansaction의 getTransaction을 사용해 트랜잭션을 가져오고
begin메서드를 통해 트랜잭션을 시작한다.
트랜잭션을 사용하기때문에 try-catch문 블록을 사용하여 오류 발생시 롤백을 수행해줘야하며, 트랜잭션이 커밋되지않으면 어떠한 변경사항도 반영되지 않는다.
사용한 entity매니저와 entitiymanagerFactory를 종료해준다
pk로 entity를 구분한다고 했는데 identifier가 그 식별자라고 이해하면된다.
Hibernate: /* insert com.sparta.entity.Memo */ insert into memo (contents, username, id) values (?, ?, ?)
하이버네이트가 위처럼 쿼리를 날려준다.
Memo memo = new Memo(); // 저장할 Entity 객체를 생성합니다. // memo.setId(1L); // 식별자 값을 넣어줍니다. memo.setUsername("Robbie"); memo.setContents("영속성 컨텍스트와 트랜잭션 이해하기"); em.persist(memo); // EntityManager 사용하여 memo 객체를 영속성 컨텍스트에 저장합니다. et.commit(); // 오류가 발생하지 않고 정상적으로 수행되었다면 commit 을 호출합니다.
- 영속성 컨텍스트의 기능
영속성 컨텍스트의 주요 기능에는 캐시저장소(1차 캐시), 쓰기 지연 저장소, 변경 감지 이 세가지가 있다 - 캐시 저장소
영속성 컨텍스트는 1차 캐시를 통해 엔티티 객체를 관리한다, 엔티티가 영속성 컨텍스트에 저장되면, 이 객체는 1차 캐시에 저장되게 된다.
캐시 저장소는 Map 자료구조 형태로 되어있으며
key는 @Id 어노테이션으로 매핑한 기본키를 저장하며
value는 Entity객체가 들어있다.
키는 #1 처럼 되어있고 value는 객체가 들어있는것을 볼 수 있다. - 조회
find()를 사용해서 조회 할 수 있다.
캐시 저장소에 id가 존재하지 않을경우
find 메서드를 통해 엔티티를 조회할때는 먼저 1차 캐시에서 조회를 한다, 만약 엔티티가 존재하지 않는다면 영속성 컨텍스트는 select쿼리를 날려 엔티티를 조회한다. 조회된 엔티티는 1차캐시에 저장되며, 이후 find를 통해 똑같은 엔티티를 찾아도 1차캐시만 조회하게 된다.
SELECT 쿼리를 날린것을 볼 수 있다.Hibernate: select m1_0.id, m1_0.contents, m1_0.username from memo m1_0 where m1_0.id=? memo.getId() = 1 memo.getUsername() = Robbie memo.getContents() = 영속성 컨텍스트와 트랜잭션 이해하기
캐시 저장소에 ID가 존재할 경우
조회를 수행할때 이미 캐시에 존재할경우는 SELECT쿼리 없이 바로 전달해준다
Memo memo1 = em.find(Memo.class, 1); System.out.println("memo1 조회 후 캐시 저장소에 저장\n"); Memo memo2 = em.find(Memo.class, 1); System.out.println("memo2.getId() = " + memo2.getId()); System.out.println("memo2.getUsername() = " + memo2.getUsername()); System.out.println("memo2.getContents() = " + memo2.getContents());
위 코드에서 find로 1을 찾은 후 캐시저장소에 저장하고
그 다음 memo2도 find로 id값 1을 찾는다면?
이렇게 쿼리문이 하나만 날라가게된다.
- 삭제
캐시 저장소에 없다면
캐시 저장소에 없다면 select쿼리를 날려 조회를 한후, 캐시저장소에 저장을 한다.
그후 remove를 사용후 delete쿼리를 db에 다시 날린다.
- 캐시 저장소의 장점
DB접근 횟수 줄일 수 있음 : 위 조회 예시처럼 캐시저장소에 이미 등록된 ID라면, DB의 접근을 하지 않아도 된다.
객체 동일성 보장
아래 예시를 보자
Memo memo1 = em.find(Memo.class, 1); Memo memo2 = em.find(Memo.class, 1); Memo memo = em.find(Memo.class, 2); System.out.println(memo1 == memo2); //ture System.out.println(memo1 == memo); //false et.commit();
memo1과 memo2는 각각 id 1을 조회한다.
memo는 id 2를 조회한다
이론상 자바의 관점에서는 주소값을 가지기때문에 equals 메서드를 사용한것이 아니라면
위는 false가 나와야겠지만, jpa는 객체 동일성을 보장하기때문에 같이 1을 조회한 memo1객체와 memo2객체는 서로 같다.
memo1 객체가 한번.
memo객체가 한번 조회를 한다
memo2 객체는 이미 memo1객체가 id1을 조회했기때문에 캐시저장소에 등록이 되었다.
- 쓰기 지연 저장소
JPA도 트랜잭션처럼 쓰기 지연 저장소를 만들어 SQL에 한번에 모아서 commit후 DB에 저장한다
actionQueue가 쓰기 지연 저장소이다.
객체 두개를를 persist를 했을경우에, 쓰기지연저장소에는 2개가 등록되어있다. - flush()
쓰기지연 저장소의 내용을 db에 반영하는 역할을 수행한다.
commit을 수행하지않더라도 flush를 사용하면 등록 할 수 있다. - 변경 감지 (dirty checking)
jpa에서는 업데이트를 어떻게 수행할까?
jpa는 영속성 컨텍스트에 entity를 저장할 때 최초 상태 - 스냅샷(LoadedState)를 저장한다 - 수정
Memo memo = em.find(Memo.class, 4); System.out.println("memo.getId() = " + memo.getId()); System.out.println("memo.getUsername() = " + memo.getUsername()); System.out.println("memo.getContents() = " + memo.getContents()); System.out.println("\n수정을 진행합니다."); memo.setUsername("Update"); memo.setContents("변경 감지 확인"); System.out.println("트랜잭션 commit 전"); et.commit(); System.out.println("트랜잭션 commit 후");
수정은 위 코드로 가능하다
update가 없는데 어떻게 가능한거지?
위 코드의 디버그를 보자
jpa는 영속성 컨텍스트에 엔티티를 저장할때 최초상태(loadedState)를 저장한다.
트랜잭션을 커밋하면 Entity Manager에서 먼저 flush를 호출한다
entity의 현재상태 (entityInstance)와 최초상태 - 스냅샷(loadedState) 를 비교한다.
만약 변경이 감지되었을때
update쿼리를 자동으로 생성한 후 쓰기지연 저장소에 저장을 한다.
그후 SQL을 모두 DB에 저장한다.
변경할 데이터를 조회합니다. Hibernate: select m1_0.id, m1_0.contents, m1_0.username from memo m1_0 where m1_0.id=? memo.getId() = 4 memo.getUsername() = Flush memo.getContents() = Flush() 메서드 호출 수정을 진행합니다. 트랜잭션 commit 전 Hibernate: /* update com.sparta.entity.Memo */ update memo set contents=?, username=? where id=? 트랜잭션 commit 후
오늘의 회고 & 12시간 몰입했는가?
찾아볼 내용
1~2주차 복습하면서 빠삭하게 다지기!
'TIL' 카테고리의 다른 글
20240819 본캠프 26일차 TIL (0) | 2024.08.19 |
---|---|
20240817 주말에도 나와버린 나 TIL (0) | 2024.08.17 |
20240815 본캠프 24일차 TIL (0) | 2024.08.15 |
20240814 본캠프 23일차 TIL (0) | 2024.08.14 |
20240813 본캠프 22일차 TIL (0) | 2024.08.14 |