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;
}

주의할 점

  • 콜렉션<엔티티>에 연관관계 설정 어노테이션을 이전과 똑같이 지정해두면 알 수 없음
    • 어노테이션의 설정값으로 타겟 엔티티가 무엇인지 설정해야함

git