TIL - 0709
java-was
기존 코드 분석하기
- 서버 소켓(8080 - 웹서버가 80을 사용하고, WAS는 8080 리버스 프록시)을 만들어두고 클라이언트 소켓의 연결 요청을 기다림
- 클라이언트 소켓이 연결되면 클라이언트의 요청을 처리할 핸들러(쓰레드)를 실행시킴 : 클라이언트 연결
Request 만들기
Request 담당 오브젝트 테스트하면서 만들기
- Request 생성자 설계 : InputStream을 인자로 받음
- 매번 브라우저로 요청을 보내고 HTTP 메세지를 읽지않기위해 모의 HTTP 메세지로 내용이 구성된 파일(request.txt)로도 읽을 수 있게)
- request.txt 읽기
- gradle, maven은 정해진 디렉토리에 정해진 파일을 넣어야함 : resource를 class path에 넣어두고 읽으려고 했더니 FileNotFound 예외발생
- 해당파일은 src/test/resources 하위에 위치시켜놓은 다음 읽어옴
public class Request {
.
.
.
public Request(InputStream in) {
.
.
.
}
}
public class RequestTest {
private Request request;
@Before
public void setUp() throws Exception {
request = new Request(new FileInputStream(new File(getClass().getClassLoader().getResource("request.txt").getFile())));
}
}
Request에서도 각 담당 오브젝트 만들기 - RequestHeader만 살펴보기
-
RequestLine/Header/Body 각각 파싱하는 방법이 다름 : 각각에 맡겨야지 Request에서 모든 방법을 맡고 있으면 한가지가 변경되었을 때 코드 찾기도 어렵고, 하나의 코드 변경이 다른 코드에 영향을 줄 수도 있음 - 객체 분리하자
- Pair : key와 value를 관리하는 RequestHeader의 내부 오브젝트(inner class)
- 내부적으로만 사용될 것이기때문에 innerClass로 만듦
- Map<String, String>으로도 처리할 수 있지만 조금 더 객체지향적으로 처리하기위해 key와 value를 필드로 가지는 오브젝트를 하나 더 만듦 : 역할을 수행하는 메소드를 또 만들 수 있는 것이 장점(코드를 깔끔하게 만들 수 있음)
- 객체지향적으로 처리하려면 역할을 잘게 잘게 쪼개자
class RequestHeaders {
private static final String SPLIT_REGEX = ": ";
private List<Pair> headers;
RequestHeaders() {
headers = new ArrayList<>();
}
RequestHeaders add(String headerSource) {
Pair header = parseHeader(headerSource);
if (!Objects.isNull(header) && headers.stream().noneMatch(pair -> pair.equals(header))) {
headers.add(header);
}
return this;
}
String getHeader(String key) {
return headers.stream().filter(header -> header.isMatch(key)).findFirst().map(Pair::get).orElseThrow(HeaderNotFoundException::new);
}
private Pair parseHeader(String headerSource) {
if (Strings.isNullOrEmpty(headerSource)) {
return null;
}
String[] token = headerSource.split(SPLIT_REGEX);
if (token.length != 2) {
return null;
}
return new Pair(token[0], token[1]);
}
private static class Pair {
String key;
String value;
Pair(String key, String value) {
this.key = key;
this.value = value;
}
boolean isMatch(String key) {
return this.key.equals(key);
}
String get() {
return value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Pair pair = (Pair) o;
return Objects.equals(key, pair.key);
}
@Override
public int hashCode() {
return Objects.hash(key);
}
}
}
JPA
JPA?
- 객체지향과 RDBMS 사이의 패러다임 차이를 없애주는 API
- 인터페이스 - 구현체가 존재함
연관관계
기본 정보
- RDBMS에서는 테이블 간의 JOIN을 의미함
- 외래키(fk)를 가지고 있다가 조회 - 작업(갱신)함
- 외래키 : RDBMS에서 다른 테이블의 행(row)를 식별할 수 있는 키(테이블의 레코드)
- 연관관계 설정에는 방향과 관게가 설정되어야함
- JPA는 @Annotation을 통해 두가지를 설정함
방향
- 단방향 : 회원 -> 팀 OR 팀 -> 회원
- 양방향 : 회원(주인) -> 팀 AND 팀 -> 회원
- 양방향 모두가 서로를 알고 있는 상태라고 보면 됨 : 각각이 접근할 수 있음
- 양방향 관계에서는 주인(연관관계 중 하나가 테이블의 외래키를 관리하고있음)이 되는 엔티티가 있음 : 모두에서 외래키를 관리하고 있지않음(설정되어있는 연관관계 주인객체가 있으니)
- 주인이 아닌 엔티티에서 연관관계의 엔티티에 수정을 가해도 아무런 변화없음(무시)
team.getMembers().add(member1); // (X)
member.setTeam(team); // (O)
관계
- N:1
- 1:N
- 1:1
- M:N
연관관계 설정 어노테이션
@ManyToOne
: N:1 설정@JoinColumn
: 외래키 맵핑
@Entity
public class Member {
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
@OneToMany
: 1:N 설정- 설정값 mappedBy : 양방향 맵핑일 때 사용하는 속성, 관계를 설정하고 있는 반대쪽의 필드명을 값으로 설정하면 됨 - 아래 예시에서 Member.team 이라 team을 값으로, mappedBy를 사용하는 쪽이 양방향 연관관계에서 주인이 아닌 쪽(읽기만 가능함 - 연관관계에서 수정할 수 없음), 보통 N일 때 외래키(연관관계 주인)를 가짐
@Entity
public class Member {
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
@Entity
public class Team {
@OneToMany(mappedBy = "team")
private List<Member> members;
}
주의할 점
- 콜렉션<엔티티>에 연관관계 설정 어노테이션을 이전과 똑같이 지정해두면 알 수 없음
엔티티>
- 어노테이션의 설정값으로 타겟 엔티티가 무엇인지 설정해야함