모델링 연습 리뷰

23
모델링 연습 리뷰 신림프로그래머 최범균 2014-11-25

description

모델링 연습한 내용 리뷰 자료입니다.

Transcript of 모델링 연습 리뷰

Page 1: 모델링 연습 리뷰

모델링 연습 리뷰

신림프로그래머 최범균 2014-11-25

Page 2: 모델링 연습 리뷰

요구사항 1 •  요청 메시지의 A옵션이 1인 클라이언트C1과 B옵

션이 2인 클라이언트 C2는 서브넷S1의 IP 대역에서 네트워크 설정(IP 포함)을 할당받는다.

•  맥주소가 M3인 클라이언트C3와 M4인 클라이언트 C4는 서브넷S2의 IP 대역 중에서 앞 쪽 절반에서 네트워크 설정을 할당받는다.

•  요청 메시지의 E옵션이 5인 클라이언트C5와 맥주소가 M6인 클라이언트C6은 서브넷S2의 IP 대역 중에서 뒤 쪽 절반에서 네트워크 설정을 할당받는다.

•  맥주소 일치가 우선한다. •  임대 IP는 클라이언트 단위로 관리된다.

2  

Page 3: 모델링 연습 리뷰

그림으로 보면

C1  

C2  

C3  (M3)  

C4  (M4)  

C5  

IP  대역    S1  

IP  대역    S2  

A=1  

B=1  

C6  (M6)  

E=1  

3  

Page 4: 모델링 연습 리뷰

그림에서 도출

C1  

C2  

C3  (M3)  

C4  (M4)  

C5  

IP  대역    S1  

IP  대역    S2  

A=1  

B=1  

C6  (M6)  

E=1  

옵션으로  클라이언트  구분  

맥주소로  클라이언트  구분  

다른 조건을 따르는 클라이언트들이 한 대역으로 묶임  

한 대역을  나눠 쓸 수  있음  

4  

Page 5: 모델링 연습 리뷰

요구사항 2 •  응답 옵션 –  IP 대역 별로 응답 옵션을 다르게 설정 –  IP 풀 별로 응답 옵션을 다르게 설정 – 클라이언트 그룹(클래스) 별로 응답 옵션을 다르

게 설정 – 특정 조건을 충족하는 클라이언트들 별로 응답 옵

션을 다르게 설정 – 같은 응답 옵션이 존재할 경우 우선 순위

•  클라이언트별 > 클라이언트 그룹 > IP 풀 > 대역 •  네트워크 설정(DNS, GW, 서브넷마스크) –  IP 대역 별로 네트워크 설정–  IP 풀 별로 다른 네트워크 설정

•  IP 풀에 네트워크 설정이 있을 경우, 우선 적용

5  

Page 6: 모델링 연습 리뷰

최초 끄적임  

6  

Page 7: 모델링 연습 리뷰

영역 구분

7  

Page 8: 모델링 연습 리뷰

설정 영역  

8  

Page 9: 모델링 연습 리뷰

Optional 써 봄

9  

public  interface  ClientFinder  {          OpFonal<?  extends  Client>  find(DhcpMessage  message);  }    public  interface  Client  {          public  OpFonal<ClientClass>  getClientClass();          ...  }    public  class  ClientClass  {          private  Pool  pool;          public  OpFonal<Pool>  getPool()  {                  return  OpFonal.ofNullable(pool);          }          ...  }  

Page 10: 모델링 연습 리뷰

사용 가능 IP 범위 찾기 public  class  UsableIpRangeFinderImpl  implements  UsableIpRangeFinder  {          private  ClientFinder  clientFinder;            @TransacFonal          @Override          public  RangeResult  find(DhcpMessage  message)  {                  if  (message  ==  null)  throw  new  IllegalArgumentExcepFon();                    OpFonal<?  extends  Client>  clientOpt  =  clientFinder.find(message);                  OpFonal<Pool>  poolOpt  =  clientOpt.flatMap(c  -­‐>  c.getClientClass()).flatMap(cc  -­‐>  cc.getPool());                  if  (!poolOpt.isPresent())  return  emptyResult();                    Pool  pool  =  poolOpt.get();                  return  new  RangeResult(                                  pool.getIpRange(),  getNetworkConfig(pool),                                  getMergedOpFons(clientOpt.get()));          }            private  NetworkConfig  getNetworkConfig(Pool  pool)  {                  NetworkConfig  nc  =  pool.getNetworkConfig();                  return  nc  !=  null  ?  nc  :  pool.getSubnect().getNetworkConfig();          }            private  DhcpOpFons  getMergedOpFons(Client  client)  {                  ...  //  다음 장에 코드 표시          }  

10  

//  OpFonal  대신 null  사용 경우  Client  client  =  clientFinder.find(message);  if  (client  ==  null)  return  emptyResult();  if  (client.getClientClass()  ==  null)            return  emptyResult();  Pool  pool  =  client.getClientClass().getPool();  if  (pool  ==  null)  return  emptyResult();  

Page 11: 모델링 연습 리뷰

응답 옵션 생성 부분

11  

//  UsableIpRangeFinderImpl    클래스  private  DhcpOpFons  getMergedOpFons(Client  client)  {          DhcpOpFons  cOpFons  =  client.getDhcpOpFons();          ClientClass  clientClass  =  client.getClientClass().get();          DhcpOpFons  ccOpFons  =  clientClass.getDhcpOpFons();          Pool  pool  =  clientClass.getPool().get();          DhcpOpFons  poolOpFons  =  pool.getDhcpOpFons();          DhcpOpFons  subnetOpFons  =  pool.getSubnect().getDhcpOpFons();            return  subnetOp9ons.merge(poolOpFons).merge(ccOpFons).merge(cOpFons);  }  

//  DhcpOpFons  클래스  public  DhcpOpFons  merge(DhcpOpFons  other)  {          DhcpOpFons  newOpFons  =  new  DhcpOpFons(this.opFonMap);            if  (other  ==  null  ||  other.isEmpty())                  return  newOpFons;            other.opFonMap.values().forEach(ov  -­‐>  newOpFons.add(ov));          return  newOpFons;  }  

Page 12: 모델링 연습 리뷰

클라이언트 찾기 •  두 종류의 클라이언트 매칭

12  

public  class  HardwareAddressClient  implements  Client  {          private  HardwareAddress  hardwareAddress;          ...  }  

public  class  MatchClient  implements  Client  {          private  List<MatchPredicate>  predicates  =  new  ArrayList<>();            public  boolean  match(DhcpMessage  message)  {                  return  predicates.stream()                                                                                .allMatch(p  -­‐>  p.match(message));          }          ...  }  

1. 하드웨어 주소  

2.  조건 일치  

Page 13: 모델링 연습 리뷰

클라이언트 찾기

13  

public  class  ClientFinderImpl  implements  ClientFinder  {          private  HardwareAddressClientRepository  hardwareAddressClientRepository;          private  MatchClientRepository  matchClientRepository;            @Override          public  OpFonal<?  extends  Client>  find(DhcpMessage  message)  {                  if  (message  ==  null)  return  OpFonal.empty();                                    //  맥주소 일치가 먼저                  OpFonal<HardwareAddressClient>  hwAddrClient  =                                    hardwareAddressClientRepository.findByHardwareAddress(message.getChaddr());                  if  (hwAddrClient.isPresent())                          return  hwAddrClient;                // 맥주소 일치 없으면, 조건 충족하는 Client  검색                  List<MatchClient>  clients  =  matchClientRepository.findAll();                  for  (MatchClient  mclient  :  clients)                          if  (mclient.match(message))                                  return  OpFonal.of(mclient);                    return  OpFonal.empty();          }          ...  

Page 14: 모델링 연습 리뷰

MatchClient의 MatchPredicate •  다양한 MatchPredicate 가능성 – 인터페이스/종류별 하위 타입으로 설계

14  

Page 15: 모델링 연습 리뷰

JPA 적용 •  DB 연동은 JPA를 사용하기로 결정 – 기본 데이터 타입은 단순 매핑 설정 사용 –  InetAddress 등에 커스텀 변환기 사용

•  적용하기 위한 몇 가지 코드 변경 – DhcpOptions 필드 à List<DhcpOption> – MatchPredicate 계층 à 단일 클래스

15  

Page 16: 모델링 연습 리뷰

일부 매핑 설정 예

16  

@Entity @Table(name = "SUBNET_CONFIG") public class SubnetConfig { @Id @Column(name = "ID") private Long id; @Column(name = "SUBNET") @Convert(converter = SubnetConverter.class) private Subnet subnet; @Embedded private NetworkConfig networkConfig; ...// DhcpOptions는 다다다...음 장에

@Embeddable public class NetworkConfig { @Column(name = "SUBNETMASK") @Convert(converter = SubnetMaskConverter.class) private SubnetMask subnetMask; @Column(name = "GATEWAY") @Convert(converter = InetAddressConverter.class) private InetAddress gateway; @Column(name = "DNSLIST") @Convert(converter = IpListConverter.class) private IpList domainServers;

SUBNET_CONFIG  -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐  ID:  int  SUBNET:  VARCHAR  SUBNETMASK:  VARCHAR  GATEWAY:  VARCHAR  DNSLIST:  VARCHAR  

Page 17: 모델링 연습 리뷰

커스텀 변환기 •  값 타입과 DB 한 개 컬럼 간의 변환 위함

17  

@Entity @Table(name = "SUBNET_CONFIG") public class SubnetConfig { @Id @Column(name = "ID") private Long id; @Column(name = "SUBNET") @Convert(converter = SubnetConverter.class) private Subnet subnet; ... } public class Subnet { private InetAddress networkAddress; private int bits; ... }

SUBNET_CONFIG  -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐  ID:  int  SUBNET:  VARCHAR  SUBNETMASK:  VARCHAR  GATEWAY:  VARCHAR  DNSLIST:  VARCHAR  

Java  new  Subnet("192.168.0.1",  24)  

컬럼 값  192.168.0.1/24  

Page 18: 모델링 연습 리뷰

커스텀 컨버터 구현 예

18  

@Converter public class SubnetConverter implements AttributeConverter<Subnet, String> { @Override public String convertToDatabaseColumn(Subnet subnet) { if (subnet == null) return null; return subnet.toString(); } @Override public Subnet convertToEntityAttribute(String dbData) { if (dbData == null || dbData.isEmpty()) return null; return new Subnet(dbData); } }

Page 19: 모델링 연습 리뷰

커스텀 컨버터 구현 예

19  

// List<InetAddress>와 "123.0.2.1,192.168.0.254" DB 데이터 간 변환 처리 @Converter public class IpListConverter implements AttributeConverter<IpList, String> { @Override public String convertToDatabaseColumn(IpList attribute) { if (attribute == null || attribute.isEmpty()) return ""; else return attribute.toString(); } @Override public IpList convertToEntityAttribute(String dbData) { if (dbData == null) return null; String[] ips = dbData.split(","); List<InetAddress> addresses = new ArrayList<>(); for (String ip : ips) { try { addresses.add(InetAddress.getByName(ip)); } catch (UnknownHostException e) { throw new RuntimeException(....생략, e); } } return new IpList(addresses); } }

Page 20: 모델링 연습 리뷰

JPA 적용 과정에서의 모델 변화 •  DhcpOptions 구현 변경 – 모델과 DB간 불일치

20  

@Entity @Table(name = "CLIENT_MATCH") public class MatchClient implements Client { // @Embedded ?? private DhcpOptions options; public DhcpOptions getDhcpOptions() { return options; } ... } // @Embeddable ?? public class DhcpOptions { // ?? private Map<DhcpOption, OptionValue> optionMap; }

CM_DHCP_OPTIONS  -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐  CM_ID:  int  LIST_INDEX:  int  OPTION_CODE:  int  OPTION_VALUE:  String  

키  vs  인덱스  

관리 화면에서 입력한 순서대로 보여줄 필요

Page 21: 모델링 연습 리뷰

JPA 적용 과정에서의 모델 변화 •  DhcpOptions 구현 변경 – 모델과 DB간 불일치

21  

@Entity @Table(name = "CLIENT_MATCH") public class MatchClient implements Client { private DhcpOptions options; public DhcpOptions getDhcpOptions() { return options; } ... }

@ElementCollection @CollectionTable(name = "CLIENT_MATCH_DHCP_OPTION", joinColumns = @JoinColumn(name = "CLIENT_MATCH_ID")) @OrderColumn(name = "LIST_INDEX") private List<OptionValue> options = new ArrayList<>(); public DhcpOptions getDhcpOptions() { // 메서드 시그너쳐 유지 return new DhcpOptions(options); }

CM_DHCP_OPTIONS  -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐  CM_ID:  int  LIST_INDEX:  int  OPTION_CODE:  int  OPTION_VALUE:  String  

Page 22: 모델링 연습 리뷰

JPA 적용 과정에서의 모델 변화 •  MatchClient 구현 변경 – @Embeddable 타입의 상속 지원하지 않음 – 계층을 단일 클래스로 변경

22  

Page 23: 모델링 연습 리뷰

23  

끝