본문으로 건너뛰기

· 약 1분
Green

redux-saga 를 처음 접하면서 generator 로 구현된 내용을 보고 정리해봅니다. ES6+ 맛보기 할때 대충 배워서 인지 역시나 정확한 동작이 이해 안되더라고요.

참조는,

표현식은 참조한 링크를 통해서 십분 이해 할 수 있을 테고, 활용 범위나 사례는 redux-saga 를 완전히 이해하고 나면 뭔가 궁리할 수 있지 않을까 싶어요.

그 후에 계속 이어 갈게요.

// TODO

· 약 3분
Green

김영한님의 자바 ORM 표준 JPA 프로그래밍 으로 스터디를 하면서 정리한 내용이었는데, 찾아보기 쉽게 간략화 해봅니다.
github 에 예제 소스도 올라가 있고요.

상속 방식

RDB 상에서 상속의 개념으로 Entity를 만들 때 @Inheritance 를 사용하잖아요.
그런데 방식이 3가지가 있어서, 각각 다른 테이블과 대응하게 되더라고요.

1. JOINED

각 Entity 별로 따로 테이블이 만들어 지는 방식이에요.

@Entity
@Inheritance(strategy = InherianceType.JOINED)
@DiscriminatorColumn(name = "MEMBER_TYPE")
abstract class Member {
@Id
@Column(name = "MEMBER_ID")
private Long id;

private String username;
private Integer password;
...
}
@Entity
@DiscriminatorValue("ADMIN")
class Admin extends Member {
@Column(name = "DEPT_CD")
private String department;
}
@Entity
@DiscriminatorValue("SELLER")
class Seller extends Member {
private String company;
}
@Entity
@DisciriminatorValue("CUSTOMER")
class Customer extends Member {
private String email;
}

이렇게 되면 다음과 같은 관계를 가지게 되는 거죠.

Member@DiscriminatorColumn(...) 을 생략 할 수 있어요.
상속받는 대상을 구분할 값이 없다면 굳이 넣지 않아도 되는거죠. 당연히 @DiscriminatorValue(...) 도 안써도 되겠네요.

2. SINGLE TABLE

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminationColumn(name = "MEMBER_TYPE")
class Member {
@Id
@Column(name = "MEMBER_ID")
private Long id;

private String username;
private String password;
private String company;

@Column(name = "DEPT_CD")
private String department;

private String email;
}

이런식으로 하나의 테이블에 몽땅 넣는 거에요.
나머지 클래스는.

@Entity
@DiscriminatorValue("ADMIN")
class Admin extends Member {
}
@Entity
@DiscriminatorValue("SELLER")
class Seller extends Member {
}
@Entity
@DiscriminatorValue("CUSTOMER")
class CUSTOMER extends Member {
}

그러면 Schema는 이렇게 생성됩니다.

3. TABLE PER CLASS

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
abstract class Member {
@Id
@Column(name = "MEMBER_ID")
private Long id;

private String username;
private String password;
}

어차피 Entity 가 상속 대상 항목별로 따로 생겨날 거니까 @DiscriminationColumn(Value) 는 필요 없군요.

@Entity
class Admin extends Member {
@Column(name = "DEPT_CD")
private String department;
}

@Entity
class Customer extends Member {
private String email;
}
@Entity
class Seller extends TablePerClassMember {
private String company;
}

이렇게 하면, 이렇게 Schema 나옵니다.
이 전략은 UNION ALL 을 사용하게 되어서 비추 되는 것 같고요.

· 약 3분
Green

정규표현식에서 앞쪽 또는 뒷쪽의 조건에 따라서 검색 방식을 다르게 할 수 있는데, 이걸 매번 까먹네요😥

1. Positive Lookahead

regexp1(?=regexp2)
"save me, save me. I need your love before I fall, fall."
.replaceAll("(save me|fall)(?=\.)", "(x2)");

결과는 save me, (x2). I need your love before I fall, (x2). 이렇습니다.
즉, regexp2 가 정방향(읽어나가는 방향)에 있을 경우 regexp1 이 검색 됩니다.

2. Nagative Lookahead

regexp1(?!regexp2)
"save me, save me. I need your love before I fall, fall."
.replaceAll("(save me|fall)(?!\.)", "(..)");

결과는 (...), save me. I need your love before I (...), fall. 이렇습니다. 즉, regexp2 가 정방향(읽어나가는 방향)에 없을 경우 regexp1 이 검색 됩니다.

3. Positive Lookbehind

(?<=regexp1)regexp2
"save me, save me. I need your love before I fall, fall."
.replaceAll("(?<=,)\s*(save me|fall)", "(x2)");

결과는 save me, (x2). I need your love before I fall, (x2). 이렇습니다.
즉, regexp1 가 역방향(읽어나가는 반대 방향)에 있을 경우 regexp2 이 검색 됩니다.

4. Nagative Lookbehind

(?<!regexp1)regexp2
"save me, save me. I need your love before I fall, fall."
.replaceAll("(?<!(,[ ]))(save me|fall)", "(..)");

결과는 save me, (x2). I need your love before I fall, (x2). 이렇습니다.
즉, regexp1 가 역방향(읽어나가는 반대 방향)에 있을 경우 regexp2 이 검색 됩니다.

conclusion

정리 한다고 했는데, 더 헷갈리게 한건 아닌지 싶군요.
그리고 IntelliJ 나 Eclipse 를 쓰신다면 RegexpTester(http://myregexp.com) 라는 플러그인을 사용해보면 정규표현식 작성에 도움이 될까 싶어요.

정규표현식 자체가 원래부터 많은 규칙들이 얽힌거니까, 우리... 잘 못해도 자신감 잃지 말아요 🤟

· 약 3분
Green

Generic Programming은 간단히 정의하기가 쉽진 않은 것 같아요. 하지만 중요한 건,

하나의 값이 여러 다른 데이터 타입들을 가질 수 있는 기술에 중점을 두어 재사용성을 높일 수 있는 프로그래밍 방식

이라는 거겠죠.

객체 상속에 의한 제네릭

그런 의미에서 자바는 기본적으로 java.lang.Object 를 묵시적 상속 하니까 그냥 제네릭이 가능하다는 얘기가 되네요.

Object obj = new Integer(1);
System.out.println((Integer)obj + 1); // 2

이렇게 슈퍼타입 Object 에 서브타입 Integer 가 할당 되니까 이런식으로 쓰면 되는 거죠.

아, 근데. 저건 좋은 방식은 아녜요.

  1. 컴파일 시점(혹은 IDE 를 이용해 작성시점)에 오류 검출이 안되고
  2. 코드상에서 실제 데이터의 타입 확인이 안되고 (캡슐화 되어 있는 필드 같은걸로 가정해보면 타입은 Object 인데 데이터는 Integer 가 들어갔는지 String 이 들어가 있는지...🤔)
  3. 타입 캐스팅을 하는게 코드가 맘에 들지 않아요.

제네릭 표현

그래서 JDK5 부터 제네릭 표현을 쓸 수 있게 되었네요.

List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add("1"); // error!
...

JDK7 부터는 타입추론이 되니까 new ArrayList<>() 이렇게도 작성할 수 있죠.

List 가 데이터를 담는 동그란 원형의 통조림 통이라 가정해보면, List<Square> 는 네모 모양의 통이라고 생각하면 될 것 같아요.

동그라미는 원형 통에만 들어가고, 네모는 네모난 통에만 들어가는거죠.

결론

제네릭을 씁시다!
코드에서 타입을 추론 할 수 있고, 컴파일 시점의 오류를 일찍 검출해서 Type safe 한 프로그래밍을 할 수 있게 도와주니까요.