네로개발일기

개발자 네로의 개발 일기, 자바를 좋아합니다 !

'2021/12'에 해당되는 글 16건


반응형

# 인터페이스는 구현하는 쪽을 생각해 설계하라.

생각할 수 있는 상황에서 불변식을 해치지 않는 디폴트 메서드 작성은 어렵다.

* 디폴트 메서드는 구현 클래스에 대해 아무 것도 모른채 합의없이 무작정 '삽입'될 뿐이다.
* Java8: 컬렉션 인터페이스 다수에 디폴트 메서드 추가
* 범용적으로 구현되어 있지만, 모든 구현체와 어울리는 것은 아니다.

## 1. 기존 인터페이스에 default 메서드를 추가함으로써 발생하는 위험
모든 상황에서 불변식을 해치지 않는 디폴트 메서드를 작성하기란 어렵다.

- 자바 8부터 Collection 인터페이스에 removeIf() 디폴트 메서드 추가
  default boolean removeIf(Predicate<? super E> filter) {
    Objects.requireNonNull(filter);
    boolean removed = false;
    final Iterator<E> each = iterator();
    while (each.hasNext()) {
      if (filter.test(each.next())) {
        each.remove();
        removed = true;
      }
    }
    return removed;
  }
 
- 람다를 활용하기 위해 추가된 메서드
public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("test");
    list.add("test2");
    list.add("java Spring");
    list.add("java Spring Data JPA");

    SynchronizedCollection<String> sc = SynchronizedCollection.synchronizedCollection(list);

    Predicate<String> startJava =  s-> s.startsWith("java");
    sc.remove("test"); //SynchronizedCollection 가 재정의한 remove() 메서드 호출
    sc.removeIf(startJava::evaluate); // Collection 에 정의된 removeIf() 디폴트 메서드  호출

}
 
- SynchronizedCollection 클래스는 말 그대로 멀티 스레드 환경에서 안정성을 보장해주는 클래스
- java8 이후 추가된 removeIf 디폴트 메서드를 재정의하지 않았기 때문에 멀티 스레드 환경에서 실행하면 예기치 못한 결과 발생

## 정리
- 디폴트 메서드라는 도구가 생겼더라도 인터페이스를 설계할 때는 세심한 주의를 기울여야 한다.
- 새로운 인터페이스라면 릴리즈 전에 반드시 테스트를 거쳐야 한다.
- 인터페이스를 릴리즈한 후라도 결함을 수정하는게 가능한 경우도 있겠지만, 그 가능성에 기대서는 안된다.
728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

# 추상 클래스보다는 인터페이스를 우선하라.


자바의 다중 구현 메커니즘: 둘 다 인스턴스 메서드를 구현 형태로 제공할 수 있다. (default method)
* 인터페이스: 다중 상속, 같은 타입 취급
* 추상 클래스: 단일 상속, 하위 클래스(상하 관계)

## 1. 인터페이스의 장점
1. 기존 클래스에도 손쉽게 새로운 인터페이스를 구현해 넣을 수 있다.
* 인터페이스: 요구하는 메서드를 추가하고, 클래스 선언에 implements 구문만 추가하면 된다.
* 추상 클래스: 계층 구조상 확장시킨 클래스의 공통 조상이 되어, 클래스 계층 구조를 생각해야한다.

2. mixin(믹스인) 정의에 안성맞춤이다.
* 믹스인: 클래스가 자신의 본래 타입에 추가하여 구현할 수 있는 타입
* 믹스인 인터페이스는 어떤 클래스의 주 기능 이외에 믹스인 인터페이스 기능을 추가적으로 제공하게 해주는 효과를 준다.

- 믹스인의 대표적인 인터페이스: Comparable, Cloneable, Serializable
public class Mixin implements Comparable {
  @Override
  public int compareTo(Object o) {
    return 0;
  }
}

3. 계층 구조가 없는 타입 프레임워크를 만들 수 있다.
public interface SingerSongWriter extends Singer, SongWriter {
    void strum();
    void actSensitive();
}
public abstract class Singer {
    abstract void sing(String s);
}

public abstract class SongWriter {
    abstract void compose(int chartPosition);
}

public abstract class SingerSongWriter {
    abstract void strum();
    abstract void actSensitive();
    abstract void Compose(int chartPosition);
    abstract void sing(String s);
}
 
- 추상 클래스로 만들면 다중 상속이 불가능해 새로운 추상 클래스를 만들어 클래스의 계층을 표현할 수밖에 없다.
- 따라서 계층 구조를 만들기 위해 많은 조합이 필요하고 결국 조합이 폭발한다.

4. 래퍼 클래스 관용구와 함께 사용하면 인터페이스는 기능을 향상 시키는 안전하고 강력한 수단이 된다.
- 타입을 추상 클래스로 정의했을 때: 기능 추가 방법은 상속뿐이다. -> 활용도가 떨어진다.
- 래퍼 클래스의 활용도가 더 높다.

## 2. 인터페이스의 디폴트 메서드 제약
- 디폴트 메서드를 제공할 때는 @implSpec을 붙여 문서화한다.
- equals 와 hashCode는 디폴트 메서드로 정의하면 안된다.
- 인터페이스는 인스턴스 필드를 가질 수 없다.
- public이 아닌 정적 멤버도 가질 수 없다.
- 우리가 만들지 않은 인터페이스에는 디폴트 메서드를 추가할 수 없다.

## 3. 인터페이스와 추상 골격 구현 클래스
1. 개념
- 인터페이스: 타입 + 디폴트 메서드
- 골격 구현 클래스: 나머지 메서드들까지 구현
- 인터페이스 구현에 필요한 대부분의 일들이 완료된다. -> 템플릿 메서드 패턴
- 네이밍 관례: Abstract[Interface명]: AbstractCollection, AbstractSet, AbstractList, AbstractMap

2. 장점
- 추상 클래스처럼 구현을 도와주는 동시에, 추상 클래스로 타입을 정의할 때 따라오는 심각한 제약에서 자유롭다.

## 4. 시뮬레이트한 다중 상속(simulated multiple inheritance)
- 골격 구현 클래스를 우회적으로 이용하는 방식
- 인터페이스를 구현한 클래스에서, 골격 구현을 확장한 private 내부 클래스를 정의하고, 각 메서드 호출을 내부 클래스의 인스턴스에 전달한다.

## 5. 골격 구현 클래스 작성 방법
1. 인터페이스를 잘 살펴 다른 메서드들의 구현에 사용되는 기반 메서드를 선정
2. 기반 메서드들을 사용해 직접 구현할 수 있느 메서드들을 모두 디폴트 메서드로 제공
3. 기반 메서드나 디폴트 메서드로 만들지 못한 메서드가 남아있다면, 이 인터페이스를 구현하는 골격 구현 클래스를 만들어 작성
4. 골격 구현은 기본적으로 상속이므로, 설계 및 문서화 지침을 모두 따라야 한다.

## 6. 단순 구현
- 골격 구현의 작은 변종
- 골격 구현처럼 상속을 위해 인터페이스를 구현했으나 추상 클래스가 아니다.


728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

💻  OS: macOS m1

✅  node version: 12.22.1

 

오류

mac m1에서 react 프로젝트를 실행시키기 위해 npm start 명령어를 사용

다음과 같은 오류 발생

FATAL ERROR: wasm code commit Allocation failed - process out of memory

 

검색해본 결과 mac m1에서 node version 15를 제외하고 잘 작동하지 않는다고 한다. 14도 되는데 12는 오류가 생기는 것 같다.

 

react-refresh-webpack-plugin과 webpack5 충돌하여 발생하는 것으로 보이고 (사실 필자는 react에 대해 아무것도 모른다...)

development에서 FAST_REFRESH 가 true가 디폴트로 실행된다. 

 

해결

아래 명령어를 사용해 해결하였다.

$ env FAST_REFRESH=false npm start

 

출처

https://github.com/pmmmwh/react-refresh-webpack-plugin/issues/259

 

Apple Silicon / arm64 / M1 compatibility? · Issue #259 · pmmmwh/react-refresh-webpack-plugin

Hi, We're using & loving react-refresh-webpack-plugin - thank you! Recently Apple Silicon came out with arch64-based CPUs. Somehow, this plugin breaks on that architecture with a strange er...

github.com

https://github.com/facebook/create-react-app/issues/10090

 

npm start gives error · Issue #10090 · facebook/create-react-app

Npm start throws error. Project was created with npx create-react-app FATAL ERROR: WasmCodeManager::Commit: Cannot make pre-reserved region writable Allocation failed - process out of memory Error ...

github.com

참고

https://dev-momo.tistory.com/entry/React-Fast-Refresh

 

React Fast Refresh

최근에 React 프로젝트르 세팅하다가 React-Hot-Loader 문서를 보니, React Fast Refresh라는 새로운 도구가 나온 것을 발견했다. React-Hot-Loader를 만든 페이스북 개발자 Dan Abramov는, 기존 핫 로더에 한계가..

dev-momo.tistory.com

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

- spring boot가 지원하는 DB migration tool은 Flyway와 Liquibase가 대표적

Flyway

- 오픈소스 마이그레이션 툴
- SQL 스크립트의 변화를 추적하면서 자동적으로 테이블의 스키마나 데이터의 변경 이력을 관리

스프링 부트에서 Flyway 사용
🌱 spring boot 2.6.0
🐥 mysql 5.7
🍿 jdk 8

- 의존성 추가
1) maven

# pom.xml 

<dependency> 
    <groupId>org.flywaydb</groupId> 
    <artifactId>flyway-core</artifactId> 
    <version>5.2.4</version> 
</dependency>

참고
- 공식문서에는 MySQL 5.7버전은 flyway 유료버전인 Flyway Teams을 사용해야 한다고 되어있다. version을 5.2.4로 정해주었더니 되어 version을 따로 기입하였다.

2) gradle

# build.gradle

dependencies {
	implementation: 'org.flywaydb:flyway-core:5.2.4'
}


- 프로젝트 구조

resource 디렉터리에 db.migration 디렉터리를 생성해서 V0__init.sql 파일을 만든다.
- 경로를 변경하고 싶을 때는 application.yml 파일에서 아래와 같이 변경해주면 된다.

spring: flyway: location: [파일위치]

sql 파일을 생성할 때 파일 이름을 정해진 기준에 따라 생성해야 한다.
=> File Naming

  • prefix: default로 V 는 버전 마이그레이션, R은 반복 마이그레이션용 접두사이다. 반드시 V또는 R로 시작해야만 flyway가 인식.
  • version: version은 버전 마이레이션에서만 사용되며 숫자와 Dots(점)이나 underscore(언더바) 조합으로 구성한다. (반복 마이그레이션에서 (version을 명시하면 filename제약 위반으로 에러 발생)
  • separator: 설명부분을 구분하기 위한 구분자이며 반드시 undersocre(언더바)를 2개( __ ) 써야한다.
  • description: 이 부분은 schema_version테이블에 저장시 설명으로 사용된다.
  • suffix: 확장자 기본은 .sql

- application.yml 파일 내용 추가

spring: datasource: url: jdbc:mysql://localhost:[port]/[schema명]?characterEncoding=UTF-8 username: [username] password: [password] driver-class-name: com.mysql.jdbc.Driver flyway: enabled: true baseline-on-migrate: true

- spring.flyway.baseline-on-migrate

  • 기본값: false
  • false: flyway_schema_history 테이블이 있는 경우
  • true: flyway_schema_history 테이블이 없는 경우 생성

이외에도 설정 정보들을 변경할 수 있다. 참고

- Spring boot Run
spring boot를 실행하면 DB schema에 flyway_schema_history 테이블이 생성되면서 migration이 자동으로 실행됩니다.
아래는 관련 로그입니다. migration이 잘 실행되었는지 확인할 수 있습니다.

INFO 49320 --- [ restartedMain] o.f.c.internal.database.DatabaseFactory : Database: jdbc:mysql://localhost:3306/[schema명] (MySQL 5.7) INFO 49320 --- [ restartedMain] o.f.core.internal.command.DbValidate : Successfully validated 1 migration (execution time 00:00.139s) INFO 49320 --- [ restartedMain] o.f.core.internal.command.DbMigrate : Current version of schema `[schema명]`: 0 INFO 49320 --- [ restartedMain] o.f.core.internal.command.DbMigrate : Schema `[schema명]` is up to date. No migration necessary.

- flyway_schema_history 테이블
flyway_scheme_history 테이블은 flyway에서 형상관리를 위하여 자동으로 생성되는 테이블입니다.

  • version: 파일의 V 뒤에 붙어있던 숫자로 낮은 순서부터 실행되며 실행 순서대로 테이블에 쌓이는 구조를 가집니다.
  • checksum: 파일의 내용을 hashing한 것입니다. 만약 파일의 내용이 달라지면 이 체크섬이 달라지게 됩니다. 한번 체크섬을 만들어 둔 후 파일을 수정한다면 그렇게 되면 누군가에 의해서 형상관리에 문제가 생겼다고 판단하기 때문에 flyway는 에러처리를 하게됩니다. 이럴 경우 해당 파일에 대한 체크섬을 repair한 후 success를 0으로 돌리는 등의 작업이 필요합니다.
  • success: 파일 실행에 성공했는지 여부를 나타내는 값입니다. 이 값에 따라서 flyway에서 해당 파일의 내용을 실행할지 말지 결정합니다.














참고
https://flywaydb.org/documentation

 

Homepage - Flyway

Version control for your database. Robust schema evolution across all your environments. With ease, pleasure, and plain SQL.

flywaydb.org

https://sabarada.tistory.com/193

 

[flyway] flyway를 통해 DDL 형상관리를 하자 - Spring Boot (Java API) 편

안녕하세요. 오늘은 flyway를 이용하여 로컬 환경에서 DDL의 형상관리를 하는 방법을 알아보도록 하겠습니다. flyway flyway는 데이터베이스의 형상관리를 목적으로 하는 툴입니다. 데이터베이스의

sabarada.tistory.com

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

jenv를 설치해 JDK 버전을 유동적으로 관리하기 

 

💻 OS: macOS m1

 

brew 설치

brew를 사용해 jenv를 설치할 것이다.

$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

jenv 설치

$ brew install jenv

Shell 환경정보에 설정 정보 추가

Shell 환경정보에 jenv 관련 설정 정보를 추가한다. 이때 Shell 종류(bash, zsh)에 따라 명령어가 달라지니 주의하자.

# Shell: bash
$ echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(jenv init -)"' >> ~/.bash_profile

$ source ~/.bash_profile
# Shell: zsh
$ echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.zshrc
$ echo 'eval "$(jenv init -)"' >> ~/.zshrc

$ source ~/.zshrc

JDK 8 설치

jdk 8은 이제 brew를 통해 설치할 수 없다.

# 다운받을 수 없음
$ brew cask install java8
> Error: Cask 'java8' is unavailable: No Cask with this name exists.

=> 대안

1. OracleJDK 8 설치

https://www.oracle.com/java/technologies/downloads/#java8

2. AdoptOpenJDK 설치

# AdoptOpenJDK 를 설치하기
$ brew tap AdoptOpenJDK/openjdk
$ brew cask install adoptopenjdk8

jenv에 versions 추가

$ jenv add /Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home
$ jenv add /Library/Java/JavaVirtualMachines/openjdk-12.0.1.jdk/Contents/Home

version 확인

$ jenv versions
* system
  1.8 (set by /Users/jyjeon/.jenv/version)
  1.8.0.292
  openjdk64-1.8.0.292

jenv 사용하여 jdk 버전 지정

global 설정을 사용하면 모든 터미널 창에 동일한 JDK 버전을 설정할 수 있고

local 설정을 사용하면 터미널에 위치하고 있는 폴더 기준으로 하위 폴더에 JDK 버전을 설정할 수 있다.

# JDK 8로 global 설정
$ jenv global 1.8.0.292

# JDK 11로 global 설정
jenv local 11.0.1

jdk 버전 확인

# JDK 8 을 사용하겠다고 설정 했을때 
$ jenv global 1.8

$ jenv versions
  system
* 1.8 (set by /Users/jyjeon/.jenv/version)
  1.8.0.292
  openjdk64-1.8.0.292
  
  # 아래 명령어로 한번더 확인할 것
  $ java -version
728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !