프론트 개발자분과 함께 작업하면서 백엔드 api서버를 로컬에 띄워 작업하는 데 한계를 느껴

아직 개발 중이지만 지금까지 만들어 둔 것만 EC2에 올려두기로 했다.

 

그런데 이미 EC2에 올려놓은 코드를 수정해야 할 작업이 계속 생기고,

그때마다 배포를 하는 것은 말도 안된다 생각하여 자동배포를 하기로 결심! 

 

동료의 도움과 아래 잘 정리된 포스팅으로 생각보다 빠르게 (반나절) 끝이 났다.

 

 

https://twofootdog.tistory.com/37

 

AWS CodeBuild로 빌드 후 S3에 빌드 결과파일 업로드

이번 글에서는 AWS CodeCommit에 있는 스프링부트 소스코드를 AWS CodeBuild를 통해서 빌드를 수행한 후 빌드된 결과파일(아티팩트(JAR))을 S3에 업로드하는 실습을 진행해볼 것이다. 이 글의 순서는 다음과 같다...

twofootdog.tistory.com

https://jojoldu.tistory.com/281

 

1) AWS로 배포하기 시리즈 - 1. Code Deploy 사용하기

AWS로 전체 시스템 구축해야 할 일이 생겨 AWS 배포 환경 시리즈를 시작합니다. 시리즈 과정은 CodeDeploy -> Code Pipeline -> ELB & Auto Scaling Group -> Beanstalk 으로 진행될 예정입니다. Code Pipeline vs..

jojoldu.tistory.com

일단 돌아가게는 만들었지만, 다시 정리할 필요를 느껴 짧게 내 방식대로 정리해보려 한다. 

 

 

 

 

 

AWS에서 배포하는 데 필요한 서비스

 

  • Code Commit
    • 깃허브와 같은 코드 저장소의 역할
  • Code Build
    • 빌드용 서비스, 규모가 있는 데에선 젠킨스/팀시티 등 이용
    • 소스코드 컴파일, 단위테스트, 배포 준비된 artifact file 생성
  • Code Deploy
    • 배포 서비스, 위 2개의 서비스는 대체재가 있으나 CodeDeploy는 대체재가 없다.
    • 오토 스케일링 그룹 배포, 블루 그린 배포, 롤링 배포, EC2 단독 배포의 기능을 지원한다. 

 

내가 이해한 Code Pipeline이 빌드 및 배포하는 과정 

 

Code Commit에 올린 내 소스가 업데이트(커밋)되면 파이프라인이 실행된다. (커밋 = 트리거)

소스 단계의 artifact가(빌드 준비가 완료된 파일)이 S3에 저장된다.  (빌드 준비가 완료된 파일)

CodeBuild는 이 artifact를 빌드한 다음, 빌드된 jar파일을 다시 S3에 저장하고, CodeDeploy에게 배포 명령을 내림

CodeDeploy는 CodeBuild가 빌드한 jar파일을 가져와서 애플리케이션(EC2)에 배포한다. 

 

 

 

 

자동배포 작업순서 

 

  • Code Commit에 내 스프링부트 소스 올려두기
  • AWS S3 버킷 생성하기
  • AWS CodeBuild 프로젝트 생성 
    • 소스공급자 - CodeCommit
    • 리포지토리 - CodeCommit 내 나의 프로젝트 
    • 브랜치 - master
    • 운영체제 - Amazon Linux 2
    • 아티팩트 - Amazon S3 (버킷 연결)
      • 패키징 : zip 
      • 캐시 : 빌드에 필요한 의존성들을 캐시하는 건데, 빌드할 때마다 새로 받는게 아니라 S3에 올려놓고 이미 있는 건 새로 받지 않도록 하는 것
  • 프로젝트 Root 디렉토리에 buildspec.yml 파일 생성
    • artifacts작성하면서 jar파일 어디에 저장하고 Deploy때 필요한 appspec.yml과 배포스크립트 파일 어디있는지 작성하기
  • commit & push 후 CodeBuild에서 빌드 시작 클릭해서 잘 되는지 테스트
  • AWS CodeDeploy용 IAM 역할 생성 (AWSCodeDeployRole)
  • AWS EC2 인스턴스에서 S3 접근을 위한 IAM 역할 생성 (AmazonEC2RoleforAWSCodeDeploy)해서 EC2에 역할주기
  • EC2 인스턴스에 CodeDeployAgent 설치
$ sudo yum update
$ sudo yum install ruby
$ sudo yum install wget

$ cd /home/ec2-user
$ wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
$ chmod +x ./install
 

최신 버전의 CodeDeploy 에이전트를 설치하려면 다음을 수행합니다.

$ sudo ./install auto

서비스가 실행 중인지 확인하려면 다음 명령을 실행합니다.

$ sudo service codedeploy-agent status

"error: No AWS CodeDeploy agent running"와 같은 메시지가 표시되면 서비스를 시작하고 다음 두 명령을 한 번에 하나씩 실행합니다.

$ sudo service codedeploy-agent start
$ sudo service codedeploy-agent status

 

출처 : https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/codedeploy-agent-operations-install-linux.html

 

  • AWS CodeDeploy 애플리케이션 생성
    • AWS CodeDeploy용 IAM 역할 붙여주기
    • 애플리케이션 배포 방법 선택 (현재위치, 블루/그린)
    • 연결할 ec2를 tag를 입력하여 알려주기
  • 프로젝트 Root 디렉토리에 appspec.yml 파일 생성
    • 어떤 파일을 어느 위치에 배포하고 어떤 스크립트 실행할 건지 관리한다.
  • 배포스크립트 start.sh 생성 (아래 참고)
  • commit & push 후 CodeDeploy에서 배포 생성해서 잘 되는지 테스트해보기

+ deploy할 때 에러가 발생할 때는 ? 

  • cd /var/log/aws/codedeploy-agent 로 가서 codedeploy-agent.log 파일을 확인해보자.

 

 

CI/CD 흐름

설정한 배포 프로세스

 

작성파일 

 

  • buildspec.yml
version: 0.2

phases:
  install:
    runtime-versions:
      java: corretto8
  build:
    commands:
      - echo starting build stage
      - mvn package
  post_build:
    commands:
      - pwd
artifacts:
  files:
    - target/*.jar
    - appspec.yml
    - scripts/**
  discard-paths: yes
 cache:
  paths:
    - '/root/.m2/**/*'

artifacts는 빌드된 빌드된 WAR/JAR를 보관할 위치를 나타낸다.

artifacts 파일에 있는 appspec.yml과 scripts/** 는 CodeBuild가 .zip으로 패키징 할 때 jar파일에 appspec.yml, start.sh를 포함시킨다. CodeDeploy는 S3에 있는 .zip파일 압축을 풀어서 appspec.yml파일을 참고해서 배포한다. 

cache 경로에 있는 파일들이 S3 캐시파일로 등록된다.

 

 

  • appspec.yml
version: 0.0
os: linux
files:
  - source: /
    destination: /home/ec2-user/target/
permissions:
  - object : /
    pattern: "**"
    owner: ec2-user
    group: ec2-user

hooks:
  AfterInstall:
    - location: start.sh
      timeout: 60
      runas: ec2-user

 

  • 배포스크립트 (.sh)
#!/bin/bash
BUILD_JAR=$(ls /home/ec2-user/target/*.jar)
JAR_NAME=$(basename $BUILD_JAR)
echo "> build 파일명: $JAR_NAME" >> /home/ec2-user/deploy.log
echo "> debugging : $BUILD_JAR"    >> /home/ec2-user/deploy.log

echo "> build 파일 복사" >> /home/ec2-user/deploy.log
DEPLOY_PATH=/home/ec2-user/target/
# cp $BUILD_JAR $DEPLOY_PATH

echo "> 현재 실행중인 애플리케이션 pid 확인" >> /home/ec2-user/deploy.log
CURRENT_PID=$(pgrep -f $JAR_NAME)
echo "> debugging : $JAR_NAME"    >> /home/ec2-user/deploy.log

if [ -z $CURRENT_PID ]
then
  echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다." >> /home/ec2-user/deploy.log
else
  echo "> kill -15 $CURRENT_PID"
  kill -15 $CURRENT_PID
  sleep 5
fi

DEPLOY_JAR=$DEPLOY_PATH$JAR_NAME
echo "> DEPLOY_PATH, JAR_NAME : $DEPLOY_PATH$JAR_NAME"    >> /home/ec2-user/deploy.log

echo "> DEPLOY_JAR 배포"    >> /home/ec2-user/deploy.log
nohup java -Xms1024m -Xmx2048m -XX:NewRatio=2 -server -Dspringi.profiles.active=product -jar $DEPLOY_JAR >> /home/ec2-user/deploy.log 2>/home/ec2-user/deploy_err.log &

 

Code Pipeline으로 배포환경을 구축할 때 발생했었던 에러

  • 평소 Maven Wrapper을 사용해서 ./mvnw로 빌드했는데, 이게 안먹는거 아닌가;
    • ./mvnw은 안되는 걸로... (아무리 검색해봐도 관련 자료가 없다 ㅠㅠ)
    • mvn package로 하자..
  • '기본 manifest 속성이 없습니다' 오류
    • Code Pipeline에서는 모든 과정이 오류 없이 Success가 떴는데, 서버가 실행되지 않았다.
    • 문제는  jar파일이 처음부터 잘못 만들어진 것인데
    • 원인은 'spring-boot-maven-plugin' 플러그인 추가를 하지 않았던 것
      • spring-boot-maven-plugin
        • 실행 가능한 jar 파일을 만들어 준다. 
          • 실행가능한 jar파일
            • 컴파일된 클래스 뿐만 아니라 코드 실행에 필요한 모든 jar dependency들을 포함하여 압축한다. 
        • 의존하고 있는 라이브러리 들을 추가해준다. 
        • BOOT-INF에 컴파일된 class 파일을 저장하는데, JarLauncher는 이 BOOT-INF 구조에 있는 class파일과 .jar파일을 로딩한다. 

 

 

public void atomicInteger1() {
    AtomicInteger atomic = new AtomicInteger();
    System.out.println("value : " + atomic.get()); // 0

    AtomicInteger atomic2 = new AtomicInteger(10);
    System.out.println("value : " + atomic2.get()); // 10
}

Java에는 Concurrent 문제를 해결하는 데 3가지 방법이 있다.

  1. volatile 이용 
  2. Atomic 클래스 이용
  3. synchronized 이용
  • volatile
    • 왜 필요한가?
      • 각각의 컴퓨터에서 어플리케이션을 실행하면, 각 쓰레드는 main memory에서 읽어온 변수 값을 각 CPU의 Cache로 복사하여 읽는다.  
      • 각 쓰레드가 같은 java 변수값을 가져왔다고 쳐도, 쓰레드가 CPU Cache에 저장된 값을 변경하고 메모리에 제때 쓰지 않는다면 각 쓰레드에서 사용하는 변수값이 일치하지 않을 수가 있다.
        • 한 쓰레드는 해당 변수를 카운트하고, 다른 쓰레드는 해당 변수를 ++ 한다고 가정하면, 카운트하는 쓰레드는 CPU캐시에만 업데이트 된 값을 가져올 수 없다는 것. 
      • 변수의 가시성 문제를해결할수있다.
        • 가시성 문제
          • CPU 메모리와 Main 메모리 중에서 어디서 가져온 값인지 알 수 없다.
          • 여러 스레드에서 변수에 대한 변경 사항의 가시성을 보장 
    • volatile변수는 컴퓨터의 메인 메모리부터 read하고, 메인 메모리에 직접 write 한다. 
    • 어떻게 쓰는가?
private volatile int count = 0;
  • atomic
    • CAS(Compare and Swap) 방식 
      • 변수의 값을 변경하기 전에 기존의 값이 내가 예상하던 값과 같을 때만 새로운 값으로 할당
public void atomicInteger2() {
    AtomicInteger atomic = new AtomicInteger();
    System.out.println("value : " + atomic.get()); // 0

    atomic.set(100);
    System.out.println("value : " + atomic.get()); // 100
}
  • atomic vs volatile
    • volatile 키워드는 오직 한개의 쓰레드에서 쓰기 작업을 하고, 다른 쓰레드는 읽기작업만을 할 때 사용
    • AtomicBoolean, AtomicInteger는 여러 쓰레드에서 읽기/쓰기 작업을 병행할 수 있다.
  • synchronized

출처

https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html

https://rightnowdo.tistory.com/entry/JAVA-concurrent-programming-Visibility%EA%B0%80%EC%8B%9C%EC%84%B1

http://tutorials.jenkov.com/java-concurrency/volatile.html

https://readystory.tistory.com/53

https://codechacha.com/ko/java-atomic-integer/

 

 

개인적인 일로 2020년이 되어서 도통 개발 공부할 시간이 없었다.

아니 남는 시간에 하려다 보니 공부할 시간이 없었던 거라고 볼 수 있다.

 

오늘 사수님이 공유해주신 포스팅을 보고 내가 진짜 개발자가 맞나라는 생각이 들었고,

나도 같은 자바 개발자로서 해당 포스팅 주인공 분과 같이 개발 책을 열심히 공부하기로 마음 먹었다. 

 

친절하시게도, 공부하신 책들을 다 소개해주셔서 나는 내 수준에 맞는 책들만 순서대로 보면 될 것 같다. 

 

개발자가 직업이라면 공부는 필수다.

 

조금씩 천천히 꾸준하게!

이 중에서도 제일 중요한 건 꾸준함이라 생각한다.

 

그래서 나의 남은 2020년의 개발 포부를 책 리스트업과 함께 해보려 한다.

조금 정리가 필요하기에 계속 업데이트 하겠다.

 

-매일 아침 최소 30분, 최대 1시간 개발 공부를 한다. 

 

 

 

-자바의 정석을 최소 2번 읽는다. 

- Spring MVC을 hotire님이 정리하신 spring-core를 보고 이해한다. 

 

-모던자바인액션 (70% 완료)

-자바 ORM 표준 JPA 프로그래밍 (50% 완료)

-개발자가 반드시 알아야 할 자바 성능 튜닝 이야기 (60%완료)

- 운영체제 (-)

https://github.com/hotire/spring-core

 

오늘 사수님께 공유받은 이직과 관련된 포스팅을 보고 급 반성을 하게 되었다.

아직 신입 꼬리표도 못뗀 주제에 너무 안일하게 시간을 보낸 것 같다는 생각이 들며,

그동안의 나를 되돌아보며 스스로를 질책했다. 

 

우선, 자바 기본책을 다시 정독하기 위해 자바의 정석 3rd edition을 새로 구입하여 두세번 읽어보기로 다짐. 

기본도 정확히 모르는 주제에 무슨 개발을 하겠다고 설쳐댄건지.

 

내가 정확하게 다 알 수 있다고 자신있게 말하려면

최소한 기본책에 나온 어떤 내용에 대한 질문에도 짧고 간결하게 답할 수 있는 정도. (구구절절? -> NO)

그 정도가 되었다는 자신이 생겼다면 지금 사두고 잘 읽지 못하고 있는 '모던 자바 인 액션'을 읽기로 하겠다. 

 

 

Anyway,

지금 어쨌든 회사에서 당장 개발해야 될 것들이 있기 때문에 책이 오기 전까지 Spring이 무엇인지, 그리고 동작원리는 어떻게 되는지 짧게라도 짚고 개발을 해야 죄책감이 아주 조금이라도 덜 것 같기에 내용이 잘 정리된 포스팅을 찾고, 그 내용을 기반으로 내 방식대로 다시 정리해보았다. 아래 나온 모든 내용은 맨 하단에 나온 출처로 부터 나온 내용!

 

 

 

  • 스프링이란?
    • IoC와 AOP를 지원하는 경량의 컨테이너
  • 컨테이너란?
    • 객체의 생성, 관리를 담당하고, 객체를 운용하는 데 필요한 기능을 제공한다. 
  • 스프링에서 컨테이너 역할을 하는 것은 ?
    • BeanFactory
    • ApplicationContext (BeanFactory 상속)
  • 두 컨테이너의 차이점은?
    • BeanFactory 
      • applicationContext.xml (스프링 설정 파일) 에 등록된 bean 객체를 생성 및 관리
      • 클라이언트로부터 요청이 들어올 때만 객체를 생성한다. 
    • ApplicationContext
      • BeanFactory의 객체 생성 및 관리 + 트랜잭션 관리 / 메시지 기반의 다국어처리 지원 
      • 컨테이너가 구동되는 시점에 bean에 등록된 클래스를 객체화 한다. 
      • GenericXmlApplicationContext -> ApplicationContext를 구현한 대표 클래스
  • 스프링의 특징
    • POJO (Plain old java object)
      • 예전에는 자바로 웹애플리케이션을 개발하려면 Servlet클래스를 상속받아 구현해야 했는데 이 Servlet이 POJO가 아니었다. 그런데 스프링을 사용하면 POJO만으로 웹 애플리케이션을 구축할 수 있게 되었다. Servlet클래스를 모두 추상화하여 라이브러리로 들어갔기 때문에. XML이나 다른 설정으로 Servlet을 이요하면 된다.  
    • IoC (Inversion of Control)
      • 객체 생성을 자바 코드로 직접 처리하는 것이 아니라, 컨테이너가 대신 처리한다. 
        • 제어권이 사용자가 아니라 프레임워크에 있다.
      • 객체와 객체 사이의 의존 관계 또한 컨테이너가 대신 처리한다. 
    • DI (Dependency Injection)
      • 의존성
        • A 객체에서 B 객체의 변수나 메소드를 사용해야 할 경우,  A라는 객체 생성자에서 new B();를 해야한다. 이 때 A는 B에 의존한다고 볼 수 있다. 
      • 의존성 주입
        • A라는 객체에서 B를 생성하는 것이 아니라 외부에서 생성된 B를 A에 주입함으로써 의존 관계를 없앤다.
      • 의존성 주입 방법
        • XML 방법
          • 생성자<constructor-arg> 태그 + ref 속성
          • 속성 <property> + name 속성  
        • Annotation 방법
          • @Autowired / @Resource 
    • AOP (Aspect of Programming)
      • 트랜잭션이나 로깅, 보안과 같이 여러 모듈에서 공통적으로 사용하는 기능의 경우, 핵심로직에서 분리하여 관리한다. 
      • AOP 방법
        • XML 방법
        • AOP 방법 
          • @Aspect / @Before
    • 스프링 동작 원리 (출처에 표시된 URL에 가시면 자세한 설명 보실 수 있습니다!!!)

웹어플리케이션 실행 -> WAS가 web.xml 실행 -> ApplicationContext를 생성하는 ContextLoaderListener가 web.xml에 등록된 내용에 따라 생성 -> root-context.xml에 등록된 Spring Container가 구동되며 필요한 객체들 생성 -> client요청 들어옴 -> DipatcherServlet 생성 -> 알맞은 Page Controller에게 전달하고 응답을 어떻게 할지 결정 -> Dispatcher Servlet은 servlet-context.xml을 loading -> 두번째 Spring Container가 구동될 때 첫번째 Container가 구동되며 생성된 DAO, VO, ServiceImple클래스들과 협업하여 작업 처리 

https://asfirstalways.tistory.com/334

 

Spring 의 시작, 프레임워크의 구성요소와 동작원리

Spring Framework의 구성요소와 동작원리 POJO 스프링의 특징을 살펴보면 POJO라는 단어가 등장한다. POJO란 Plain Old Java Object로 직역하자면 평범한 옛날 자바 객체이다. 말 그대로 자바 객체인 것이다. 이..

asfirstalways.tistory.com

 

 

  • 정의
    • 클래스 내부에서 사용할 데이터 타입을 클래스를 인스턴스화 하는 시점에 지정하는 기법
      •  = 내부에서 사용할 데이터 타입을 외부에서 지정
  • 사용하는 이유
    • 코드 재사용성 UP! -> 반복코드 DOWN 
      • 하나의 예를 들면, 데이터 타입이 다르다는 이유로 같은 로직인데도 매개변수 타입별로 코드를 다시 쓰지 않아도 된다. 
    • 타입안정성 
      • 제네릭을 사용하면 런타입에 발생하는 에러를 피하고 컴파일 단계에서 에러를 잡을 수 있다.
      • 의도하지 않은 데이터 타입이 오는 것을 피할 수 있다. (아래 예제에서 보이는 것과 같이 rank를 갖고 있지 않은 경우엔 에러를 뱉어내게 할 수 있으니까)
      • 강제 데이터 타입 변환을 막을 수 있다. 
class EmployeeInfo{
    public int rank;
    EmployeeInfo(int rank){ this.rank = rank; }
}
class Person{
    public Object info;
    Person(Object info){ this.info = info; }
}
public class GenericDemo {
    public static void main(String[] args) {
        Person p1 = new Person("부장");
        EmployeeInfo ei = (EmployeeInfo)p1.info;
        System.out.println(ei.rank);
    }
}

 

컴파일은 잘 되는데 런타임에 에러가 발생한다. 

class EmployeeInfo{
    public int rank;
    EmployeeInfo(int rank){ this.rank = rank; }
}
class Person<T>{
    public T info;
    Person(T info){ this.info = info; }
}
public class GenericDemo {
    public static void main(String[] args) {
         
        Person<String> p = new Person<String>("모디");
        String ei = p.info;
        System.out.println(ei.rank); // 컴파일 실패
    }
}

런타임에 에러가 발생한다. e1에는 rank라는 메소드가 없어서

 

  • 제네릭<T> vs wildcard <?>
    • 와일드 카드는 타입 특성에 맞게 사용할 필요가 없을때, 그 자리에 어떤게 들어와도 상관 없을 경우에 사용한다.

 

예전에 코드 작성할 때 어느 순간 제네릭을 사용해야 되겠다는 느낌이 와서 적용한 기억이 있는데

또 다시 가물가물해져서 정리해보았다. 

 

참고

 

  • for vs advanced for문
    • for에서는 index값을 이용할 수 있고, 배열의 값을 가져와 수정할 수 있다.
    • advanced for문 에서는 index 값을 이용할 수 없고, 배열의 값을 가져와 쓸 수만 있고, 수정할 수 없다. 
  • advanced for의 장단점
    • 장점 
      • 배열의 크기를 알지 못해도 OK
      • 수행속도가 조금 더 빠름 (ArrayList는 제외)
      • 코드가 짧음
    • 단점
      • 배열이나 리스트의 값 변경 혹은 추가 NO
      • 배열 역순 탐색 NO
  • (old school) for 문 vs (modern) foreach
    • 차이
      • old school for문은 외부 iterator, modern forEach는 내부 iterator
    • old school for   vs   modern foreach
      • ArrayList 와 같은 경우엔 일반적인 for루프와 forEach()와 속도 면에서 크~~게는 차이가 없다. 그럼에도 일반적인 for루프 보다는 10~20% 느리다. 
      • 그런데 primitive 타입의 array의 경우엔 일반적인 for루프가 훨씬 더 빠르다.
      • forEach()는 JVM 및 라이브러리에서 훨씬 더 많은 작업을 수행하게 되어 유지 측면에서 비효율적이다. 
    • foreach의 장점이라면?
      • 가독성이 좋아진다. (근데 그게 또 항상 그런 것은 아니다...) 
      • 병렬프로그래밍에 용이
  • Collection.forEach() vs Collection.stream().forEach()
    • Collection.forEach()는 컬렉션의 반복자를 사용한다 (지정된 경우), item의 처리 순서가 정해졌다는 것
      • hasNext(), next(), remove() 사용
    • 반면에 Collecaion.stream().forEach()는 정해지지 않았다.
      • 반복자를 무시하고 목록에서 요소를 하나씩 가져온다. 
    • 컬렉션 수정
      • 반복 중에 element가 추가 혹은 제거 되면 ConcurrentModification 예외가 발생 
        • Collection.forEach는 추가 혹은 제거 되자 마자 에러가 발생되는데 Collection.stream().forEach()의 경우는 나중에 예외를 발생시킨다. 
      • Collecion.forEach()는 element를 수정할 수 있는 반면 Collection.stream().forEach()는 수정하면 안됨
        • 둘 다 수정할 수는 있으나, stream은 병렬로 수행되기에 예기치 않은 문제가 발생할 수 있다. 
    • 결론
      • stream이 필요하지 않고 컬렉션을 반복하려는 경우엔 컬렉션에서 직접 forEach()를 사용한다.
  • 자바 8에서 형변환 하는 방법
    •  

old school for 문

     public List<Fruit> appleToFruit(String name){
		List<String> appleNames = Arrays.asList(name.split(","));
		List<Fruit> fruitList = new ArrayList<>();
		for(int i=0; i<appleNames.size(); i++) {
			fruitList.add(new Fruit(appleNames.get(i)));
		}
		return fruitList;
	}
    
    
public static List<Fruit> appleToFruit(String apple){
	List<String> appleNames = Arrays.asList(apple.split(","));
	return appleNames.stream().map(v -> new Car(v)).collect(Collectors.toList());
}

 

참고

불러오는 중입니다...

https://webfirewood.tistory.com/41?category=698497

https://webfirewood.tistory.com/38?category=698497

 

서블릿(Servlet)의 동작구조

웹 서버의 주된 기능은 웹 페이지를 클라이언트로 전달하는 것입니다. 주로 그림, CSS, 자바스크립트를 포함한 HTML 문서가 클라이언트로 전달됩니다. 하지만 이런 웹 서버의 경우 이미 존재하는 즉, 정적인 페이..

webfirewood.tistory.com

 

서블릿(servlet) 생명주기(lifecycle)

우선 지난 시간에 배웠던 Servlet의 동작 순서를 다시한 번 복습 하겠습니다. 여기서 컨테이너는 매핑된 서블릿을 찾아서 service() 메소드를 호출한다고 했습니다. 그런데 어떤 클래스의 메소드를 호출하려면..

webfirewood.tistory.com

 

ParentId id1 = new ParentId();
id1.setId1("myId1");
id1.setId2("myId2");

ParentId id2 = new ParentId();
id2.setId1("myId1");
id2.setId2("myId2");

id1.equals(id2) ??????

id1.equals(id2) 의 결과값은 false이다.

왜냐하면 자바의 모든 클래스는 기본으로 Object 클래스를 상속받는데, 이 클래스가 제공하는 기본 equals()는 인스턴스 참조 값 비교인 ==비교(동일성비교)를 하기 때문이다. 

 

영속성 컨테스트는 엔티티의 식별자를 키로 사용해서 엔티티를 관리한다. 

그리고 식별자를 비교할 때 equals() 와 hashcode()를 사용한다. 

따라서, 식별자 객체의 동등성(equals)이 지켜지지 않으면 예상과 다른 엔티티가 조회되거나 엔티티를 찾을 수 없는 등 영속성 컨텍스트가 엔티티를 관리하는 데 문제가 발생한다. 

 

따라서 복합 키는 equals()와 hashCode()를 필수로 구현해야 한다. 

 

 

* 응용 프로그램은 세션 당 단일 스레드를 고수하는 한 비즈니스 오브젝트에서 동기화 할 필요가 없습니다. 세션 내에서 응용 프로그램은 ==를 사용하여 객체를 안전하게 비교할 수 있습니다.

 

* equals와 hashcode를 어떨 때  사용하는가 ?  -> 가장 이해하기 좋았던 사이트 

https://jojoldu.tistory.com/134

 

equals와 hashCode 사용하기 ( +lombok)

안녕하세요? 이번 시간엔 equasl & hashcode를 어떤 곳에서 사용할 수 있는지를 확인해보려고 합니다. 모든 코드는 Github에 있기 때문에 함께 보시면 더 이해하기 쉬우실 것 같습니다. (공부한 내용을 정리하는 G..

jojoldu.tistory.com

해당 인스턴스들이 갖고 있는 값들이 같을 경우 같은 인스턴스로 봐야할 때 equals를 오버라이딩

equals와 hashcode는 모두 VO(value object)에서만 사용하길, 값 나타내는 것 이외에 기능을 갖는 인스턴스에서 오버라이딩을 하면 문제가 발생할 수 있음. 롬복 사용시  @EqualsAndHashCode(exclude = {"id", "paymentMethod", "price"}) 와 같은 형태로 클래스 명 위에 입력해주면 된다. 

 

* 참고

-Book. 자바 ORM 표준 JPA 프로그래밍

-https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html

 

4.3. Implementing equals() and hashCode()

4.3. Implementing equals() and hashCode() You have to override the equals() and hashCode() methods if you intend to put instances of persistent classes in a Set (the recommended way to represent many-valued associations) and intend to use reattachment of d

docs.jboss.org

https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/transactions.html#transactions-basics-identity

+ Recent posts