반응형

서브넷이 뭔가요?

서브넷은 서브 네트워크를 말하며, 기존 네트워크 영역을 분할해 더 작은 크기의 네트워크 영역으로 쪼갠 네트워크이다. (말 그대로 서브 네트워크...) AWS에서의 서브넷은 VPC 내에 생성하는데, VPC 가 가진 CIDR 블록(기존 네트워크 영역) 내에서 더 작은 CIDR 블록(더 작은 크기의 네트워크 영역) 로 쪼갠 네트워크 영역을 말한다.

 

아래와 같이 VPC를 10.0.0.0/16 으로 CIDR 대역을 설정한 후 10.0.10.0/25, 10.0.20.0/25 CIDR 블럭을 갖는서브넷을 각각 생성하였다. 즉, A 서브넷은 10.0.10.0 ... 10.0.10.127 IP 대역을, B 서브넷은 10.0.20.0 ... 10.0.20.127의 IP 대역을 갖게 되는것이다.

서브넷팅

 


퍼블릭 서브넷, 프라이빗 서브넷??

서브넷 유형을 말하며, 이 두 유형은 서브넷을 생성할 때 지정하는게 아니라 서브넷에 할당된 라우팅 테이블에 의해 지정된다. 퍼블릭의 의미는 인터넷 망, 정확히 말하면 인터넷 게이트웨이와 직접 연결되어 있는 것을 말한다.

 

다시말하면 퍼블릭 서브넷은 라우터에 의해 인터넷 게이트웨이와 직접 연결되는 서브넷이고, 프라이빗 서브넷은 인터넷 게이트웨이와 직접 연결되지 않은 서브넷이다. 라우팅 테이블 설정에 따라 퍼블릿 서브넷이 프라이빗 서브넷이 될수도, 그 반대가 될수도 있다.

 

퍼블릭 서브넷과 브라이빗 서브넷

 

 


인터넷 게이트웨이가 뭔가요?

인터넷 게이트웨이란 VPC와 인터넷 간에 통신할 수 있게 해주는 VPC 구성요소를 말한다. 줄여서 IGW라고 부른다.

 


라우팅 테이블에 IGW만 추가하면 외부와 통신이 가능한건가요?

IGW에 대한 라우팅 정보를 추가하기만 하면 해당 서브넷 내 리소스들이 인터넷 망으로 나갈 수 있을까? 그렇지 않다. 서브넷 내에 퍼블릭 IP를 가진 리소스가 존재하지 않기 때문이다.

 인터넷 게이트웨이를 설정했다면, NAT 게이트웨이를 설정하거나, 서브넷 상의 리소스에 퍼블릭 IP를 할당해야만이 인터넷 게이트웨이를 통해 인터넷에 연결할 수 있다.

반응형
반응형

AWS VPC 설명서 기반으로 스터디한 내용입니다.

https://docs.aws.amazon.com/ko_kr/vpc/latest/userguide/what-is-amazon-vpc.html

 

Amazon VPC란 무엇인가? - Amazon Virtual Private Cloud

이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.

docs.aws.amazon.com

 

개요

 예전 AWS 로 인프라를 구성한 적이 있는데, 구성 당시에는 누군가 만들어놓은 메뉴얼을 따라하기만 했지, 각각의 리소스들이 왜 필요한지, AWS 내에서 어떤 역할을 하는지에대한 고민은 하지 않았다. 그래서 이번 기회에 AWS 구조와 용어에 대해 이해하고 싶었고, 가장 기본인 VPC부터 공부하게 되었다.

 


Amazon VPC가 뭔가요?

아마존(Amazon) 에서 제공하는 격리된(Private) 가상(Virtual) 클라우드(Cloud) 서비스를 말한다. 조금 풀어 말하면, 아마존과 같은 퍼블릭 클라우드 환경 일부분을 고객 전용으로 프라이핏하게 사용할 있는 가상 네트워크 말한다. 가상 네트워크라는 말이 알쏭달쏭한데, 글을 읽다보면 이해할 수 있을것이다.

 

"VPC는 리전의 모든 가용 영역에 적용됩니다." ??

Amazon VPC 사용 설명서의 첫 시작 문구이다. 첫 시작부터 발걸음을 돌리고 싶게 만드는 알쏭달쏭한 문구인데, 첫 문구인만큼 이 의미를 이해하는 것이 중요하다고 생각했다. 먼저 리전과 가용 영역에 대해 알아보자.

 


리전(Region)과 가용영역(Availability Zone)

리전 (Region)

리전이란 전 세계에서 데이터 센터를 클리스터링하는 지리적 위치를 말한다. 여기서 지리적 위치라고 표현한 이유는 실제 물리적인 위치보다 더 넓은 범위를 표현하는 용어이기 때문이다. 누군가 "너 어디살아?" 라고 물었을때 "나 서울시 xx구 xx아파트 203동 101호에 살아" 라고 하지 않고 "나 서울살아" 라고 하는것과 같다고 생각하면 된다.

 

데이터 센터
용어만 들으면 데이터가 저장된 센터? DB를 말하는건가? 라고 착각이 들 수 있다. 클라우드 컴퓨팅에서의 데이터 센터란 컴퓨팅 시스템 및 하드웨어 장비가 저장된 물리적 위치를 말한다. 즉, 클라우딩 관련 장비가 저장된 센터를 말한다.

 

Region

 

 

위 그림을 보면 알 수 있듯이 클라우드 서비스를 제공하기 위한 데이터 센터의 지리적 위치는 미국 동부, 서부, 서울, 도쿄 등 지역별로 고루 퍼져있다.

 

가용영역 (Availability Zone)

 리전별로 하나의 데이터 센터만을 운용하고 있을까? 아니다. 최소 2개 이상의 데이터 센터를 가져야 리전으로써의 조건이 성립된다. 즉, 서울 리전에는 실제로 2개 이상의 데이터 센터들이 어딘가에서 운용되고 있는 것이다. 그리고 이렇게 퍼져있는 데이터 센터들을 논리적으로 묶어놓은 것을 가용영역이라 한다.

 

Region - AZ - IDC

 

위 그림은 리전과, 가용영역, 그리고 실제 데이터 센터를 도식화 한것이다.

 


 

"VPC는 리전의 모든 가용 영역에 적용됩니다." !!

다시 돌아가서 이 문구의 의미는 뭔지 생각해보자. 해석하면 "VPC라는 가상 네트워크는 리전 내에 있는 모든 가용영역에 위치시킬 수 있다는 뜻이고 이 말은 하나의 VPC 내에 생성되는 리소스들을 여러 데이터 센터에 위치시킬 수 있다는 뜻이다."

아래 그림과 같이 말이다. VPC가 가상의 네트워크이기 때문에 여러 가용영역을 공유 사용할 수 있는것이다.

 

여러 AZ에 접근가능한 VPC

 

 

VPC 에는 자체 IP가 없고 IP 대역(CIDR) 을 설정하던데...

 VPC를 생성한다고 해서 VPC 자체에 대한 IP가 할당되지 않는다. 말 그대로 "가상의 네트워크"이기 때문이다. 대신 VPC 내에 실제 리소스를 생성할 때에는 해당 리소스에 IP가 할당되는데, 이때 할당되는 IP의 범위, 즉 CIDR를 VPC 생성 시 설정해야 한다. VPC를 생성할 때 IP 프로토콜과 CIDR를 설정하는 이유가 바로 이것이다.

 

VPC 설정의 CIDR 블록 설정부분

 

IPv4 프로토콜을 사용할 경우 CIDR 블록 크기 설정 시 RFC1918 규격과 AWS 자체 VPC CIDR 블럭 규칙에 따라 CIDR를 설정해야 한다. 이를 준수하는 CIDR 블록 크기는 아래로 한정된다. 

 

10.0.0.0/16 ~ /28

172.16.0.0/16 ~ /28

192.168.0.0/16 ~ /28

 

 

RFC 1918
프라이빗 IP의 국제 규격으로 아래 대역 범위를 갖는다.

RFC 1918

 


 

달랑 VPC 만 생성해주지 않아요~

VPC를 생성하면 VPC만 생성되는게 아니라 기본 리소스인 기본 DHCP 옵션 세트, 기본 네트워크 ACL, 기본 보안그룹, 기본 라우팅 테이블도 함께 생성된다. 각각에 대해 알아보자.

 

첫째, 기본 DHCP 옵션 세트

 

DHCP가 뭔가요?

 Dynamic Host Configuration Protocol의 약자로 네트워크에 위치한 컴퓨터 및 기타 장치에 IP 주소와 같은 네트워크 정보를 자동으로 할당하기 위한 프로토콜을 말한다. 네트워크 설정을 DHCP 서버가 중앙 집중식으로 관리하는 클라이언트/서버 모델인 것이다.

 이전에는 네트워크에 위치한 컴퓨터 및 기타 장치의 IP 주소를 수동으로 할당했지만, 오늘날에는 DHCP를 사용해 동적으로 할당하고 있다. 생소하게 느껴질 수 있지만 사실 대부분의 컴퓨터 사용자들이 이 프로토콜을 사용하여 네트워크 설정을 자동화했을 것이다. 필자의 맥북또한 마찬가지인데, 네트워크 탭의 IPv4의 구성 방식이 DHCP를 사용하도록 설정되어 있어 IP, DNS서버, 게이트웨이 주소 등을 따로 설정하지 않아도 자동으로 할당되는 것을 확인할 수 있다.

필자의 맥북 네트워크 설정

 

 

DHCP를 사용하면 뭐가 좋나요?

네트워크 설정을 자동화할 수 있고, 수동 IP 할당 시 발생할 수 있는 IP 충돌 문제를 예방할 수 있다.

 

DHCP 옵션 세트가 뭔가요?

EC2 인스턴스가 실행될 때 DHCP를 통해 자동으로 네트워크 설정이 되도록 DHCP 서버로 요청하는데, 이 DHCP 관련 설정이 담긴 세트이다. 각 리전마다 각기 다른 기본 DHCP 옵션 세트를 갖고 있다.

 

DHCP 옵션 세트가 AWS에서 어떻게 쓰이는지 알려주세요

VPC 내 EC2와 같은 리소스가 실행되면 IP, DNS 서버와 같은 네트워크 설정을 위해 Amazon DHCP 서버로 요청하게 된다. 그럼 DHCP 서버는 VPC 내 설정된 DHCP 옵션 세트를 로드하게 되는데, 이 옵션 세트에 따라 IP 주소와 DNS 서버와 같은 네트워크 설정을 해당 리소스에 할당하게 된다.

 

 참고로 리소스에 네트워크 설정이 정상적으로 할당된 경우 해당 리소스는 자신에게 할당된 IP 정보를 자동으로 라우팅 테이블에 등록하게 되는데, 이러한 과정으로 인해 내부 리소스간의 네트워킹이 가능한 것이다.

 

AWS에서의 DHCP 옵셧 세트 사용 방식

 

그래서 기본 DHCP 옵션 세트는 뭐라고요?

기본 DHCP 옵션 세트란 리소스의 네트워킹 설정을 위해 DHCP 서버가 참조하는 기본옵션들을 말한다. VPC 라는 가상 네트워크 환경에 설정한 CIDR 대역에 맞는 IP로 할당되어야 하지 않겠는가? 이러한 외부 정보가 없다면 어떤 IP를 할당해야하는지에 대한 기준이 잡히지 않을 것이다. (이건 필자의 지극히 주관적인 생각입니다.)

 


둘째, 기본 네트워크 ACL

 

네트워크 ACL이 뭔가요?

네트워크 ACL이란 Network Access Control List의 약자로 '서브넷 수준'에서 특정 인바운드 또는 아웃바운드 트래픽에 대한 접근 제어 리스트를 말한다. 아래는 서브넷이 2개인 VPC 내에서 네트워크 ACL의 역할을 알려주는 그림이다.

 

네트워크 ACL

 

트래픽이 VPC로 들어오면 라우터에서 라우팅 테이블을 확인해 트래픽을 타겟으로 보낸다. 이때 네트워크 ACL로 하여금 해당 트래픽이 서브넷으로 들어가고 나갈 수 있는지를 제어하는 것이다. 네트워크 레벨에서의 방화벽인 셈이다.

 

그래서 기본 네트워크 ACL은요?

AWS에서 기본으로 제공하는 네트워크 ACL로, VPC 내 서브넷을 생성할 경우 네트워크 ACL을 설정해야 하는데, 설정하지 않을 경우 자동으로 할당되는 네트워크 ACL이다. 기본 네트워크 ACL의 정책은 모든 인바운드 및 아운바운드에 대한 IPv4, IPv6 트래픽을 허용한다.

기본 네트워크 ACL의 아웃바운드 규칙
기본 네트워크 ACL의 인바운드 규칙

 


셋째, 기본 보안그룹

 

보안그룹이 뭔가요?

보안 그룹은 VPC 내 리소스에 대한 접근을 제어하는 그룹을 말한다. 예를 들어 특정 IP 에 대한 인바운드를 차단하는 보안 그룹을 만들고, 2개의 EC2 인스턴스의 보안 그룹에 이를 적용한다면, 특정 IP가 두 EC2 인스턴스로 접근하지 못하도록 차단한다.

 

네트워크 ACL과 같은거 아닌가요?

인바운드, 아운바운드에 대한 접근을 제어한다는 점에서 비슷하지만, 적용 레벨이 다르다. 네트워크 ACL은 서브넷 레벨에서, 보안그룹은 리소스 레벨에 적용된다.

 

그래서 기본 보안그룹은요?

VPC를 생성할 경우 기본으로 제공되는 보안그룹이다. 모든 트래픽에 대해 인바운드와 아웃바운드를 허용하도록 정책이 설정되어 있다

 

보안그룹

 

위 그림은 VPC 내 위치한 두 개의 EC2가 기본 보안 그룹을 적용한 상황이다. 기본 보안그룹 설정했으니 모든 포트 및 IP로부터 오는 트래픽을 받을 수 있게 된다. 단, 기본 보안그룹은 인터넷 게이트웨이 또는 NAT 게이트웨이로부터 오는 트래픽을 거부하도록 설정되어 있다. 만약 NAT 게이트웨이나 인터넷 게이트웨이를 사용한다면 커스텀 시큐리티 그룹을 만들어 각 인스턴스에 적용하면 된다.

 


기본 라우팅 테이블

 

라우터부터 알고가자

라우터란 네트워크 데이터 전송을 위해 최적 경로를 선택한 후 네트워크간 통신 있도록 도와주는 인터넷 장비이다.

그렇다면 라우터는 어떻게 '최적의 경로' 찾아낼 수 있는걸까? 그건 바로 라우터가 가진 '라우팅 테이블' 참고했기 때문이다.

 

라우팅 테이블이 뭐에요?

라우팅 테이블이란 네트워크에서 목적지 주소를 통해 물리적 목적지에 도달하기 위한 경로들이 저장된 테이블이다. 라우터로 하여금 최적의 경로를 선택하도록 도와주는 역할을 한다.  라우터가 가진 라우팅 테이블은 목적지에 도달하기 위해 거쳐야할 다음 라우터의 정보를 가지고 있다.

 

라우팅 테이블은 누가 작성하는 건가요?

관리자가 수동으로 작성할 수도 있지만, 일반적으로 라우팅 프로토콜을 통해 자동으로 라우팅 테이블이 만들어진다. 라우팅 프로토콜(Routing Protocol)이란 라우터끼리 경로 정보를 교환하는 프로토콜로 RIP, BGP, OSPF 프로토콜이 있다.

 

그래서 기본 라우팅 테이블은요?

VPC 만들면 기본으로 적용되는 라우팅 테이블이다. 서브넷을 생성할 라우팅 테이블을 명시적으로 할당해야하는데, 이를 하지 않을 경우 이 기본 라우팅 테이블이 서브넷에 할당되게 된다.

 기본 라우팅 테이블에는 로컬 라우팅만 포함된다. 그런데 VPC 내에 NAT 게이트웨이를 생성하면 VPC 기본 라우팅 테이블에 0.0.0.0/0 트래픽에 대한 타겟 경로를 NAT 게이트웨이로 자동 추가한다. 외부로 나갈때 NAT 게이트웨이를 통해 자동으로 IP가 변경하도록 하기 위함이다. 이를 암시적 서브넷 연결이라고 한다.

 

NAT
Network Address Translation(네트워크 주소 변환)의 약자로 네트워크 주소인 IP를 변환하는 용어이다.
NAT 게이트웨이
IP를 변환시켜주는 장비를 말하며, 일반적으로 사설 네트워크에 속한 호스트가 하나의 공인 IP 주소를 사용하여 인터넷망에 접속하기 위해 사용한다.

 

 이 라우팅 테이블에 설정된 타겟에 따라 서브넷의 유형이 결정된다. 서브넷에 할당된 라우팅 테이블에 인터넷 게이트웨이가 추가되어 있을 경우 퍼블릿 서브넷, 인터넷 게이트웨이가 없다면 프라이빗 서브넷으로 구분된다. 이 내용은 아래 게시글을 참고해보면 좋다.

https://tlatmsrud.tistory.com/172#comment14007882

 

[AWS] 서브넷이란? / 퍼블릿 서브넷 / 프라이빗 서브넷

서브넷이 뭔가요?서브넷은 서브 네트워크를 말하며, 기존 네트워크 영역을 분할해 더 작은 크기의 네트워크 영역으로 쪼갠 네트워크이다. (말 그대로 서브 네트워크...) AWS에서의 서브넷은 VPC

tlatmsrud.tistory.com

 

반응형
반응형

개요

 RAG 프로세스 중 외부 데이터를 LOAD하는 단계에서 쓰이는 LangChain의 Document Loader에 대해 알아보자.

 


Document Loader의 종류

Document Loader는 다양한 소스의 데이터를 문서의 데이터로 로드하는 클래스이다. txt 파일, 웹페이지, 유튜브 비디오 스크립트, PPT, CSV 등 등 다양한 데이터를 로드할 수 있다.

 

1) Text Loader

텍스트 파일을 읽어온다. 

from langchain_community.document_loaders import TextLoader

loader = TextLoader("/content/drive/MyDrive/Colab Notebooks/langChain_document_Loader_study_file/text.txt")
loader.load()

 

[Document(metadata={'source': '/content/drive/MyDrive/Colab Notebooks/langChain_document_Loader_study_file/text.txt'}, page_content='hi hello,\nis text file\n\n안녕하세요\n텍스트 파일입니다.')]

 

 

2) CSV Loader

CSV 파일을 로드한다. CSV 파일은 IBM에서 제공하는 샘플 CSV 파일을 사용했다. 로우별로 page_content가 분리되고, \n 문자를 구분자로 컬럼에 대한 데이터들이 로더되는 것을 확인할 수 있다.

샘플 CSV 파일

from langchain_community.document_loaders.csv_loader import CSVLoader

loader = CSVLoader(file_path = "/content/drive/MyDrive/Colab Notebooks/langChain_document_Loader_study_file/Catalog_v2.csv")
loader.load()

 

[Document(metadata={'source': '/content/drive/MyDrive/Colab Notebooks/langChain_document_Loader_study_file/Catalog_v2.csv', 'row': 0}, page_content='\ufefflevelType: CATEGORY\ncode: Street Lighting\ncatalogType: PRODUCT\nname: Street Lighting\ndescription: Category code for Street Lighting\nsourceLink: http://lighttree.com/Street Lighting'),
 Document(metadata={'source': '/content/drive/MyDrive/Colab Notebooks/langChain_document_Loader_study_file/Catalog_v2.csv', 'row': 1}, page_content='\ufefflevelType: CATEGORY\ncode: Pedestrian Lighting\ncatalogType: PRODUCT\nname: Pedestrian Lighting\ndescription: Category code for Pedestrian Lighting\nsourceLink: http://lighttree.com/Pedestrian Lighting'),
 Document(metadata={'source': '/content/drive/MyDrive/Colab Notebooks/langChain_document_Loader_study_file/Catalog_v2.csv', 'row': 2}, page_content='\ufefflevelType: CATEGORY\ncode: Traffic Signal Poles\ncatalogType: PRODUCT\nname: Traffic Signal Poles\ndescription: Category code for Traffic Signal Poles\nsourceLink: http://lighttree.com/Traffic Signal Poles'),
 Document(metadata={'source': '/content/drive/MyDrive/Colab Notebooks/langChain_document_Loader_study_file/Catalog_v2.csv', 'row': 3}, page_content='\ufefflevelType: CATEGORY\ncode: Controls\ncatalogType: PRODUCT\nname: Controls\ndescription: Category code for Controls\nsourceLink: http://lighttree.com/Controls'),
 ...

 

 

3) Directory Loader

 디렉토리 내 파일들을 로드한다.현재 path 경로에 txt와 csv 파일이 있는데, 이 두가지 파일을 모두 로더하는 것을 확인할 수 있다.

from langchain_community.document_loaders import DirectoryLoader

loader = DirectoryLoader(path = "/content/drive/MyDrive/Colab Notebooks/langChain_document_Loader_study_file/", glob='*.*')
loader.load()

 

[Document(metadata={'source': '/{경로}/text.txt'}, page_content='hi hello,\n\nis text file\n\n안녕하세요\n\n텍스트 파일입니다.'),
 Document(metadata={'source': '/{경로}/Catalog_v2.csv'}, page_content='\n\n\nlevelType\ncode\ncatalogType\nname\ndescription\nsourceLink\n\n\nCATEGORY\nStreet Lighting\nPRODUCT\nStreet Lighting\nCategory code for Street Lighting\nhttp://lighttree.com/Street Lighting\n\n\nCATEGORY\nPedestrian Lighting\nPRODUCT\nPedestrian Lighting\nCategory code for Pedestrian Lighting\nhttp://lighttree.com/Pedestrian Lighting\n\n\nCATEGORY\nTraffic Signal Poles\nPRODUCT\nTraffic Signal Poles\nCategory code for Traffic Signal Poles\nhttp://lighttree.com/Traffic Signal Poles\n\n\nCATEGORY\nControls\nPRODUCT\nControls\nCategory code for Controls\nhttp://lighttree.com/Controls\n\n\nCATEGORY\nDownlights\nPRODUCT\nDownlights\nCategory code for Downlights\nhttp://lighttree.com/Downlights\n\n\nCATEGORY\nRetrofit Downlights\nPRODUCT\nRetrofit Downlights\nCategory code for Retrofit Downlights\nhttp://lighttree.com/Retrofit Downlights\n\n\nCATEGORY\nAmbient\nPRODUCT\nAmbient\nCategory code for Ambient\nhttp://lighttree.com/Ambient\n\n\nCATEGORY\nBulbs\nPRODUCT\nBulbs\nCategory code for Bulbs\nhttp://lighttree.com/Bulbs\n\n\nCATEGORY\nControllers\nPRODUCT\nControllers\nCategory code for

 

 

4) HTML Loader

 html 파일을 로드한다. 포스팅에 참고하고 있는 LangChain 페이지를 html 파일로 저장 후 로더를 통해 로드를 해봤다. 결과값을 보면 HTML 내에서 '컨텐츠'로 활용할 수 있는 HTML 태그를 제외한 내용들을 문서형식으로 추출하고 있다.

 

LangChain.html

 

from langchain_community.document_loaders import UnstructuredHTMLLoader

loader = UnstructuredHTMLLoader("/content/drive/MyDrive/Colab Notebooks/langChain_document_Loader_study_file/LangChain.html")

data = loader.load()
data

 

HTML 태그가 없는 형태

 

5) JSON Loader

json 형식의 파일을 로드한다.

 

sample.json

 

!pip install jq # 필요한 패키지 설치

from langchain_community.document_loaders import JSONLoader

import json
from pathlib import Path
from pprint import pprint

file_path='/content/drive/MyDrive/Colab Notebooks/langChain_document_Loader_study_file/sample.json'
data = json.loads(Path(file_path).read_text())
pprint(data)

결과

 

 

 

특정 노드의 값을 추출할 수도 있다. 아래는 IOUnitIDs 키에 대한 노드를 추출했다.

from langchain_community.document_loaders import JSONLoader

import json
from pathlib import Path
from pprint import pprint

loader = JSONLoader(
    file_path='/content/drive/MyDrive/Colab Notebooks/langChain_document_Loader_study_file/sample.json',
    jq_schema='.IOUnitIDs',
    text_content=False)

data = loader.load()
pprint(data)

 

[Document(
	metadata={'source': '/content/drive/MyDrive/Colab Notebooks/langChain_document_Loader_study_file/sample.json', 'seq_num': 1}
    , page_content='{"cics:ABEND-1": "EXEC CICS ABEND", "cics:ASKTIME-1": "EXEC CICS ASKTIME", "cics:FORMATTIME-1": "EXEC CICS FORMATTIME", "cics:GET CONTAINER-1": "EXEC CICS GET CONTAINER", "cics:LINK-1": "EXEC CICS LINK [LGSTSQ]", "cics:PUT CONTAINER-1": "EXEC CICS PUT CONTAINER", "cics:RETURN-1": "EXEC CICS RETURN", "pgm:PROCEDURE DIVISION-1": "PROCEDURE DIVISION", "sql:CLOSE-1": "EXEC SQL CLOSE [TESTMULTI,Zip_Cursor,CusClaim_Cursor]", "sql:FETCH-1": "EXEC SQL FETCH [Zip_Cursor]", "sql:FETCH-2": "EXEC SQL FETCH [CusClaim_Cursor]", "sql:FETCH_ROWSET_NEXT-1": "EXEC SQL FETCH ROWSET NEXT [TESTMULTI]", "sql:OPEN-1": "EXEC SQL OPEN [TESTMULTI]", "sql:OPEN-2": "EXEC SQL OPEN [Zip_Cursor,CusClaim_Cursor]", "sql:SELECT_INTO-1": "EXEC SQL SELECT INTO [POLICY,ENDOWMENT,MOTOR]", "sql:SELECT_INTO-2": "EXEC SQL SELECT INTO [POLICY,HOUSE]", "sql:SELECT_INTO-3": "EXEC SQL SELECT INTO [CUSTOMER,MOTOR]", "sql:SELECT_INTO-4": "EXEC SQL SELECT INTO [POLICY,COMMERCIAL]", "sql:SELECT_INTO-5": "EXEC SQL SELECT INTO [POLICY,COMMERCIAL]", "sql:SELECT_INTO-6": "EXEC SQL SELECT INTO [POLICY,CLAIM]"}')]

 

 

6) PDF Loader

PDF를 로드한다. 페이지별 배열 형태로 로드되며 메타 데이터로 페이지 번호를 제공한다. 

from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader("/content/drive/MyDrive/Colab Notebooks/langChain_document_Loader_study_file/sample.pdf")
pages = loader.load_and_split()
pages

 

필자의 이력서 PDF 파일을 INPUT으로 넣어본 결과 오타 없이 잘 출력되는 것을 확인했다. 하지만 PDF 내 이미지 파일 형태로 이력서가 박혀있는 경우에는 해당 내용을 읽어오지 못했다. 

 

예를들어 아래는 PDF는 내 이미지 형태로 이력서가 들어있는데, 이 경우 pageContent에 이력서 내용이 포함되지 않는다.

한국산업인력동단 제공 PDF

 

 

pageContent에 이력서 내용이 없음

 

 

참고로 PDF Loader는 종류도 많고, 각각에 대한 성능의 장단점이 존재한다고 하니 잘 찾아보고 사용하자.

 

7) WEB Loader

웹사이트를 로드하여 컨텐츠 정보를 추출한다. 아래 갤럭시 링 관련 기사에 대한 웹사이트 컨텐츠를 추출해보았다. 웹페이지 내 HTML이나 이미지는 모두 제거된, 텍스트 데이터들만 추출된 것을 확인할 수 있다.

 

갤럭시링 관련 기사

from langchain_community.document_loaders import WebBaseLoader # 웹페이지 문서를 로드하는 클래스.

# 리뷰 | 드디어 베일 벗은 삼성 ‘갤럭시 링’, 체험 후 느낀 3가지
loader = WebBaseLoader("https://www.ciokorea.com/news/344012")
data = loader.load()

data

 

 

결과 #1

 

결과 #2

 

 

 

 

이 외에도 LangChain의 Document Loader는 Markdown, Microsoft Office DOCS, XLSX, PPT 파일 등 다양한 형식의 파일에 대한 Loader를 지원하고 있다. 물론, 모두 무료는 아니다. 자세한 내용은 아래 LangChain 공식문서를 참고하여 사용하면 된다.

 


출처

LangChain 공식문서 https://python.langchain.com/v0.1/docs/modules/data_connection/document_loaders/file_directory/

 

File Directory | 🦜️🔗 LangChain

This covers how to load all documents in a directory.

python.langchain.com

 

RAG 위키독스 https://wikidocs.net/231645

 

2-2-3. 디렉토리 폴더 (DirectoryLoader)

### DirectoryLoader 이용하여 특정 폴더의 모든 파일을 가져오기 `DirectoryLoader`를 사용하여 디렉토리 내의 모든 문서를 로드할 수 있습니다. `Di…

wikidocs.net

 

반응형
반응형

개요

 검사 예외 (Checked Exception)과 런타임 예외(Unchecked Exception), 에러의 개념과 차이에 대해 숙지하고 있지만, 어떤 상황에서 이러한 예외들을 적용할지에 대한 명확한 기준은 잡혀있지 않았다. 필자의 경우 모든 예외를 런타임 예외로 던졌다. 검사 예외를 사용할 경우 동일 트랜잭션 내 예외 발생 시 롤백하지 않는다는 것이 이유였다. 이번 아이템을 통해 예외처리에 대한 보다 명확한 기준을 잡도록 하자.

 


검사 예외(Checked Exception)는 언제? 

호출하는 쪽에서 복구하리라 여겨지는 상황에 사용한다.

 

위 내용이 검사 예외와 런타임 예외를 구분하는 기본 규칙이다.

검사 예외를 던지면 호출자는 예외에 대한 처리가 강제된다. 여기서의 예외는 '회복 가능한 예외'이다. 본인이 발생시킨 예외에 대해 호출자가 회복이 가능하다고 판단된다면 검사 예외를 던지면 된다.

 

* 아마 검사 예외에 트랜잭션에 대해 알아본 독자들이라면 검사 예외일 때는 트랜잭션 롤백이 되지 않는다는 것을 알고 있을 것이다. 검사 예외가 발생했고, 로직에서 복구했는데 트랜잭션이 롤백이 된다면, 복구시킨들 무슨 소용이 있으랴... 트랜잭션과 검사 예외에 대한 관계를 생각해보면 '회복 가능한 예외'일때 검사 예외를 던지는 것을 더 쉽게 이해할 수 있을것이다.

 


비검사 예외(Unchecked Exception) 는 언제?

 호출하는 쪽에서 복구가 불가능하리라 여겨지는 상황에 사용한다.

 

비검사 예외는 회복 불가능한 예외이다. Runtime Exception 과 Error 이 두가지로 구성되는데 이 둘 모두 회복이 불가능하다고 여겨질 때 사용하며, 통상적으로 잡지(catch) 말아야 한다. 자신만의 커스텀 예외로 예외 전환을 하는 목적으로 사용하는것은 상관 없지만, 이를 잡아 '복구'시킬 필요는 없다. 오히려 복구시키면 문제가 된다. (feat. 트랜잭션)

 

이처럼 복구가 불가능한 것을 책에서는 '프로그래밍 오류'라고 칭한다.


복구 가능 여부는 어떻게 판단하나?

복구할 수 있는지 아닌지는 명확히 구분되지 않는다. 예를들어 시스템 자원이 고갈된 원인이 엄청난 양의 배열을 생성한 것이라면 프로그래밍 오류라고 할 수 있지만, 폭발적인 요청에 의해 일시적으로 자원이 부족하여 발생했다면 시간을 두고 재요청을 하는 방식으로 복구할 수 있다. 결국, 복구 가능하냐, 불가능하냐에 대한 기준. 검사 예외, 비 검사 예외를 사용하는 것에 대한 기준은 오롯이 API 설계자의 판단에 달렸다. 복구가 가능하다고 믿는다면 검사 예외를, 그렇지 않다면 런타임 예외를 사용할 것이다.

 


정리

복수할 수 있는 상황이라면 예외 검사를, 프로그래밍 오류라면 비검사 예외를 던지자. 확실하지 않다면 비검사 예외를 던지자.

반응형
반응형

개요

 예외를 예외 상황에서만 사용하지 않는 케이스에 대해 알아보고, 어떤 문제를 야기하는지 이해해보자.

 


예외 상황에서 사용하지 않는 예

MyClass[] range = new MyClass[5];
range[0] = new MyClass();
range[1] = new MyClass();
range[2] = new MyClass();
range[3] = new MyClass();
range[4] = new MyClass();

try{
    int i = 0;
    while(true){
        range[i++].myMethod();
    }
}catch (ArrayIndexOutOfBoundsException e){

}
System.out.println("종료");

 

try catch 문 안에 반복문이 있고, range 배열을 순회하며 myMethod()를 호출하고 있다. (myMethod는 단순 print 문을 호출함) 반복을 하다 range[1++] 에서 배열의 최대 길이를 넘어갈 경우 ArrayIndexOutOfBoundsException 예외가 발생하는데, 이를 예상하여 예외를 잡아 처리하고 있다. 여기서 발생한 예외는 예외 상황이라고 하기엔 민망하기에 catch 문 안에 아무런 처리를 하지 않고있다. 즉, 일반적인 제어의 흐름에 사용한 것이다.

 

뭐 어찌됐던간에 range 를 순회하여 myMethod를 호출하는 것에는 문제가 없다. 


개발자의 의도 파악하기

이를 대체할 수 있는 방법은 여러가지가 있겠지만 위 코드를 작성한 개발자의 의도는 뭐였을까? 바로 성능을 높이기 위해서이다. JVM은 배열에 접근할 때마다 경계를 넘지 않는지 검사하는데, 일반적인 반복문도 배열 경계에 도달하면 종료한다. 어쨌든 배열 경계에 도달하면 예외를 발생시켜 종료하기에 검사 코드를 제거한 것이다. 하지만 이는 잘못된 추론이다. 이에 대한 근거를 알아보자.

 


잘못된 추론

 

첫째, 예외는 예외 상황에 쓸 용도로 설계되었다.

JVM 입장에서 예외의 쓰임이 잘못된 것이다. 쓰임이 잘못됐다면, JVM이 지원하는 기능들을 사용하지 못할 확률이 높다.

 

둘째, 배열을 순회하는 표준 관용구는 앞서 걱정한 중복 검사를 수행하지 않는다. JVM이 알아서 최적화해 없애준다.

이게 JVM 이 지원하는 기능 중 하나이다.

 

셋째, 코드를 try-catch 블록 안에 넣으면 JVM이 적용할 수 있는 최적화가 제한된다.

JVM의 기능을 사용하지 못하게 되었다.

 


위 코드에 대한 성능 테스트

테스트를 해보면 예외를 사용한 쪽이 표준 관용구보다 훨씬 느리다는 것을 알 수 있다.

// 테스트 데이터 셋팅
MyClass[] range = new MyClass[10000];
for(int i =0; i<10000;i++){
    range[i] = new MyClass();
}


// 예외 사용
long start = System.currentTimeMillis();
try{
    int i = 0;
    while(true){
        range[i++].myMethod();
    }
}catch (ArrayIndexOutOfBoundsException e){

}
System.out.println("걸린 시간 : "+ (System.currentTimeMillis() - start)); // 40~43

// 표준 반복 관용구 사용
start = System.currentTimeMillis();
for(MyClass a : range){
    a.myMethod();
}

System.out.println("걸린 시간 : "+ (System.currentTimeMillis() - start)); // 20~22
System.out.println("종료");

 

일반적인 반복문이 예외처리를 한 반복문보다 2배는 빠르다. 배열을 순회하는 표준 코드를 JVM이 최적화했다는 것을 간접적으로나마 확인할 수 있었다. 성능이 개선될 줄 알았던 예외를 사용한 반복문은 오히려 느리고, 코드를 헷갈리게 (왜 이렇게 코드를 짰지? 뭐가 있나? 라는 생각이 들지 않는가) 하는데 끝나지 않는다. 버그가 발생하고 디버깅이 어려워진다.

 


디버깅을 어렵게 하는 코드

아래 코드는 try 문 내에서 호출되는 myMethod() 이며, 보면 arr[10] 에 "lastDance"라는 문자열을 입력한다. 길이가 10이니 arr[10] 에 접근하는 부분에서 ArrayIndexOutOfBoundsException 예외가 발생할 것이고, 예외 로그를 확인한 개발자는 이를 수정할것이다.

static class MyClass{

    String[] arr = new String[10];

    public void myMethod(){
        System.out.println("call my method");
        arr[10] = "lastDance";
    }
}

 

그런데 실제로는 예외 로그가 발생하지 않는다. 이를 호출한 main 메서드의 catch에 의해 아무런 처리를 하지 않기 때문이다. 개발자는 예외 로그가 찍히지 않으니 문제가 없다고 생각하고 넘어가버리게 된다. 결국 예외는 오직 예외 상황에서만 써야 한다. 절대로 일상적인 제어 흐름용으로 쓰여선 안된다.

오류 없이 실행된 것 같은(?) 로그

 

 


정리

예외는 예외 상황에서 쓸 의도로 설계되었다. 제어 흐름에서 사용해서는 안된다. 코드를 최적화하려다 오히려 망가뜨릴 수 있다.

반응형
반응형

1. 개요

 JVM의 구성요소 중 하나인 네이티브 메서드의 사용에 대한 내용이다. 아직까지 네이티브 메서드를 직접 사용하는 것에 대한 필요성을 느끼지 못하고 있지만, 직접 사용하게 될 경우 어떤 주의사항이 있는지 알아보자.

 


2. 네이티브 메서드와 JNI

 자바에서의 네이티브 메서드란 C나 C++ 같은 프로그래밍 언어로 작성된 메서드를 말한다. 그렇다면 자바에서 C와 C++ 과 같은 언어로 작성된 코드를 직접 호출할 수 있을까? 아니다. JVM 내에 두 언어 간 중간다리 역할을 하는 인터페이스가 구성되어 있다. 이를 JNI라고 한다.

 참고로 JNI 는 JVM 의 구성 요소 중 하나이다. 이에 대해 알고싶다면 아래 게시글을 읽어보면 좋다.

https://tlatmsrud.tistory.com/148#google_vignette

 

[Java] JVM, JDK, JRE / 차이 / JVM 구조

JVM이 뭐야? JVM(Java Virtual Machine)은 바이트코드(.class)를 OS에 특화된 코드(기계어)로 변환하고, 이를 실행하는 '가상의 머신'이다. 특정 OS에 특화된 코드로 변환하기때문에 OS 종속적이다. JVM은 JRE에

tlatmsrud.tistory.com

 


3. 네이티브 메서드의 사용 목적

 

1) 레지스트리와 같은 플랫폼 특화 기능을 사용하기 위해

 레지스트리는 윈도우 플랫폼에서 사용하는 기능으로, 윈도우 OS의 설정을 담고 있는 DB를 말한다. 즉, OS 설정을 건드릴 때 네이티브 메서드 사용이 하나의 선택지가 될 수 있다.

 

2) 네이티브 코드로 작성된 기존 라이브러리를 사용하기 위해

 

3) 성능 개선을 위해

 성능에 결정적인 영향을 주는 부분을 네이티브 언어로 작성하면 성능 개선을 가져올 수도 있다.

 

 하지만 성능 개선을 위한 네이티브 메서드 사용은 권장하지 않고 있다. 자바가 발전함에 따라 대부분의 작업에서 다른 플랫폼에 견줄만한 성능을 내고 있기 때문이다. 예를들어 자바 1.1 시절 BigInteger는 C로 작성한 네이티브 메서드를 JNI를 통해 사용했으나 자바 3 에서 업데이트되며 원래의 네이티브 구현보다 더 빨라졌다. 

 


4. 네이티브 메서드의 단점

 네이티브 언어는 OS를 직접 건드리기에 안정성을 보장하지 않는다. 잘못 사용할 경우 애플리케이션의 버그를 유발하거나 메모리를 훼손할 수 있다. 가비지 컬렉터가 네이티브 메모리는 자동으로 회수하지 못하고 추적할 수도 없다.

 이식성도 낮다. 자바에서 JNI를 사용하지 않으면 네이티브 메서드를 사용하지 못한다. JNI를 구성한다해도 이 과정에서 접착 코드(glue code)를 작성해야 하는데, 이는 복잡한 작업이고 가독성도 떨어진다.

 


5. 정리

 네이티브 메서드가 성능을 개선해주는 일은 많지 않다. 네이티브 코드 안에 숨은 단 하나의 버그가 애플리케이션 전체를 훼손할 수 있으므로 신중히 고민하고 사용해야한다.

반응형
반응형

개요

박싱된 기본 타입과 기본타입의 차이를 알아보고, 왜 후자의 사용을 지향하는지 알아보자.

 


기본 타입이란?

자바에서의 타입은 '데이터 타입'을 말한다. 이는 해당 데이터가 메모리에 어떻게 저장되고, 어떻게 처리되어야 하는지를 명시적으로 알려주는 역할을 한다.

자바에서는 여러 형태의 '타입'을 미리 정의하여 제공하는데, 이것을 자바의 기본타입(==Primitive type) 이라고 한다. 참고로 기본 타입 외에도 String, List 와 같은 참조 타입이 있다.

 기본 타입은 8 종류가 있으며, 크게는 정수형, 실수형, 문자형, 논리형 타입으로 나뉜다.

 


정수형 타입 (4종류)

정수란 부호를 가지고 있으며, 소수 부분이 없는 수를 의미한다. int, long, short, byte 타입이 있다.

정수형 타입 메모리의 크기 데이터의 표현 범위
byte 1바이트 -128 .. 127
short 2바이트 -32,768 ~ 32,767
int 4바이트 -2,147,483,648 ~ 2,147,483,647
long 8바이트 -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807

 

실수형 타입 (2종류)

실수란 소수부나 지수부가 있는 수를 가리키며, 정수보다 훨씬 더 넓은 표현 범위를 가진다. float, double 타입이 있다.

실수형 타입 메모리의 크기 데이터의 표현 범위
float 4바이트 3.4 * 10의 -38승 ... 3.4 * 10의 38승
double 8바이트 1.7 * 10의 -308승 ... 1.7 * 10의 308승

 

 

문자형 타입 (1종류)

컴퓨터는 2진수밖에 인식하지 못하므로 어떤 문자를 어떤 숫자에 대응시키는 약속을 했다. 대표적으로 C언어는 아스키 코드를 사용해 문자를 표현한다. 아스키 코드는 영문 대소문자 및 몇가지 기호를 사용하는 7비트 문자 인코딩 방식이다. 문자 하나를 7비트로 표현하므로 총 128개의 문자를 표현할 수 있다.

 

자바에서는 유니코드를 사용하여 문자를 표현한다. 문자 하나를 16비트로 표현하므로, 총 65,536 개의 문자를 표현할 수 있기에 각 나라의 모든 언어를 표현할 수 있다.

문자형 타입 메모리의 크기 데이터의 표현 범위
char 2 바이트 0 ... 65,536

 

 

논리형 타입 (1종류)

논리형은 참이나 거짓 중 한 가지 값만을 가질 수 있는 타입을 의미한다. boolean 타입이 있다.

boolean 형의 기본값은 false 이며, 1 바이트의 크기를 가진다.

논리형 타입 메모리의 크기 데이터의 표현 범위
boolean 1바이트 true 또는 false

 


 

박싱된 기본 타입이란?

자바에서는 앞서 말한 기본 타입에 대응하는 참조 타입이 하나씩 있는데, 이를 박싱된 기본타입이라고 한다.

예를들어 int, double, boolean 에 대응하는 박싱된 기본 타입은 Integer, Double, Boolean이다.

 


기본 타입과 박싱된 기본 타입의 차이

 

첫째, 기본 타입은 값만 가지고 있으나, 박싱된 기본 타입은 식별성이란 속성을 갖는다.

박싱된 기본 타입의 두 인스턴스는 값이 같아도 서로 다르다고 식별될 수 있다. 메모리의 주소가 다르기 때문이다.

 

둘째, 기본 타입의 값은 언제나 유효하나, 박싱된 기본 타입은 null을 가질 수 있다.

null 에 대한 체크가 이루어지지 않는다면 NPE가 발생할 수 있다.

 

셋째, 기본 타입이 박싱된 기본 타입보다 처리 시간, 메모리 사용면에서 효율적이다.

박싱된 기본 타입이 메모리를 더 잡아먹기 때문이다.

 

이 세가지를 무시하고 생각없이 사용했다가는 문제가 발생할 수 있다. 이를 무시했을 때 발생하는 문제들을 예제로 알아보자.

 


예제 1. 오름차순 정렬

Comparator<Integer> naturalOrder = (i, j) -> (i<j) ? -1 : (i == j ? 0 : 1);
System.out.println(naturalOrder.compare(new Integer(1),new Integer(3)));

 

함수형 인터페이스인 Comparator 의 구현체를 람다식으로 구현한 후, 호출하는 예제이다. i 보다 j 가 클 경우 -1, 같을경우 0, i 가 클 경우 1을 리턴한다. naturalOrder.compare 메서드의 매개변수로 값을 넣어 테스트했을 때는 문제는 발생하지 않는 것처럼 보인다. new Integer(1), new Integer(3)을 넣었을 땐 -1이, 반대로 넣었을땐 1이 리턴되기 때문이다. 그렇다면 위 코드의 문제가 뭘까?

 

 

 

바로 같은 값에 대해서는 0을 리턴하지 않는 점이다.

 

첫번째 검사 (i<j) 는 잘 동작한다. i, j가 참조하는 오토박싱된 Integer 인스턴스는 비교를 위해 기본 타입 값으로 변환된 후 비교 연산을 한다. 그런데 두 번째 검사인 (i == j) 에서는 '객체 참조'의 식별성을 검사하게 된다. i와 j 가 같은 값을 갖고있다고 할지라도 인스턴스가 다르기에 false 가 출력되고 결국 1을 반환한다. 즉, 박싱된 기본 타입에 == 연산자를 사용하게 될 경우 문제가 발생하는 것이다.

 


예제 2. 기이하게 동작하는 프로그램 예제

public class UseCase {

    static Integer i;

    public static void main(String[] args) {

        if (i == 42)
            System.out.println("믿을 수 없군!");
    }
}

 

이 프로그램의 실행 결과는 어떨까?

 

 

이 프로그램의 결과는 "믿을 수 없군!"을 출력하지 않지만, 그 전에 기이한 결과를 보여준다. i == 42를 검사할 때 NullPointerException을 던지는 것이다. 원인은 (i == 42) 코드에서 null을 참조하고 있는 참조 타입 i와 42라는 기본 타입을 비교하기 위해 i를 언박싱하게 되는데, 이때 null 참조를 언박싱하므로 NPE 예외가 발생한다. 기본 타입과 박싱된 타입을 혼용한 연산에서는 박싱된 기본 타입의 박싱이 자동으로 언박싱되기 때문이다. 

 


예제 3.느린 반복문 예제

Long sum = 0L;

for(long i = 0; i <= Integer.MAX_VALUE; i++){
    sum += 1;
}

 

이 프로그램은 지역변수 sum을 박싱된 기본 타입으로 선언하여 느려졌다. sum += 1 부분에서 언박싱과 박싱이 반복해서 일어나기 때문이다. 체감될 정도로 성능이 느려진다.

 


박싱된 기본 타입은 언제 쓰는게 좋을까?

그렇다면 박싱된 기본 타입은 아래와 같은 상황에서 써야한다.

첫째, 컬렉션의 원소, 키, 값으로 쓴다. 컬렉션은 기본 타입을 담을 수 없기때문이다.

둘째, 타입 매개변수로 사용한다. 타입 매개변수로 기본 타입을 지원하지 않기 때문이다.

셋째, 리플렉션을 통해 메서드를 호출할 때도 박싱된 기본 타입을 사용해야 한다.


정리

기본 타입과 박싱된 기본 타입 중 하나를 선택해야 한다면 기본 타입을 사용하자. 간단하고 빠르며, 앞서 말했던 언박싱으로 인한 버그, 성능 이슈를 예방할 수 있기 때문이다. 또한 기본 타입을 박싱하는 작업은 필요 없는 객체를 생성하는 부작용을 나을 수 있다. 박싱된 기본 타입을 꼭 사용해야한다면 위와 같은 문제 상황들을 이해하고 적절히 사용해야 한다.

 

반응형
반응형

적합한 인터페이스가 있다면 매개변수뿐 아니라 반환값, 변수, 필드를 전부 인터페이스 타입으로 선언하자. 실제 클래스를 사용해야 할 상황은 '오직' 생성자로 생성할 때 뿐이다. 선언 타입에 대해서는 클래스를 생각하기 전에 '인터페이스'부터 생각하는 습관을 길러야 겠다.

 

ArrayList<String> list = new ArrayList<String>();

 

생성자를 생성할 때는 클래스를 사용했고, 타입도 클래스를 사용했다. 이건 좋지 못한 코드이다.

아래와 같이 ArrayList의 인터페이스인 List를 사용하는 것이 좋다.

 

List<String> list = new ArrayList<String>();

 

 

인터페이스는 유연성을 더한다.

인터페이스를 도입했다는 것은 인터페이스를 사용하는 클래스가 구현부를 알지 못한다는 것이다. 즉, 클래스의 구현체 코드를 알지 못한다는 것이다. 클래스가 구현체 코드를 알지 못한다는 건 안좋은거 아닌가? 완전 바보 클래스 아니야? 라고 생각할 수 있지만, 반대로 생각하면 바보에게도 먹히는게 인터페이스인 것이다. 같은 이름의 과자 포장지 안에 내용물을 다르게 넣어도 되는것이다.

 

만약 위 예제에서 ArrayList 대신 LinkedList로 구현체를 교체하고싶다면, 생성자 부분만 교체하면 된다. 어차피 List<String> 타입 변수는 애초에 구현체 클래스를 알지 못했으니 이러한 변화에 영향을 받지 않게된다.

 

List<String> list = new LinkedList<String>();

 

주의할 점

원래의 클래스가 인터페이스의 일반 규약 이외의 특별한 기능을 제공하여, 주변 코드가 이 기능에 기대어 동작한다면 새로운 클래스도 반드시 같은 기능을 제공해야한다.

예를들어 LinkedHashSet이 따르는 순서 정책을 가정하고 동작하는 상황에서 HashSet으로 바꾸면 문제가 될 수 있다. HashSet은 순회 순서를 보장하지 않기 때문이다.

 

구현타입을 바꾸려는 동기

 기존 사용하던 것보다 성능이 좋거나 좋은 기능을 제공할 수 있기 때문이다. 예를들어 HashMap을 참조하던 변수가 EnumMap으로 바꾸면 속도가 빨라지고 순회 순서도 키의 순서와 같아진다. 단, EnumMap은 키가 열거 타입일 때만 사용 할 수 있다.

 또한 LinkedHashMap으로 바꾼다면 성능은 비슷하게 유지하면서 순회 순서를 보장하도록 설정할 수 있다.

 

선언 타입과 구현 타입을 동시에 바꾸면 안되나

 프로그램이 컴파일 되지 않을 수도 있다. 클라이언트에서 기존 타입에서만 제공하는 메서드를 사용했거나, 기존 타입을 사용해야 하는 다른 메서드에 그 인스턴스를 넘겼다면, 타입이 바뀌는 순간 컴파일 에러가 발생할 것이다.

 

적합한 인터페이스가 없다면?

적합한 인터페이스가 없다면 당연히 클래스로 참조해야 한다. String이나 Integer 같은 클래스가 이에 해당한다.

인터페이스에는 없는 특별한 기능을 제공하는 클래스를 사용하는 경우도 있다. 예를들어 PriorityQueue 클래스는 Queue 인터페이스에는 없는 comparator 메서드를 제공한다. 클래스 타입을 직접 사용하는 경우는 이런 추가 메서드를 꼭 사용해야 하는 경우로 최소화해야 한다.

 

정리하면

타입으로 클래스 타입을 사용하는 것보다 확장성을 고려하여 인터페이스나 가장 덜 구체적인 상위타입의 클래스 타입을 사용하는 것이 좋다. 클래스를 직접 사용하는 경우는 최소화하자.

반응형
반응형

개요

 이번 아이템은 라이브러리를 무작정 사용하지 말고, 사용법을 익힌 후에 사용하라는 것인 줄 알았는데, 알고보니 어떤 기능을 구현함에 있어 대체 가능한 라이브러리가 있다면 직접 구현하지 말고 이를 익히고 사용하라는 것이었다.

 

라이브러리를 사용했을 때의 이점

 

1. 코드를 작성한 전문가의 지식과 선배 프로그래머들의 경험을 활용할 수 있다.

 - 라이브러리 자체가 지식과 경험의 집약체인 것이다.

 

2. 비지니스 로직, 핵심 로직에 시간을 집중할 수 있다.

 - 핵심적인 일과 크게 관련없는 문제들은 라이브러리가 대체할 수 있기 때문이다.

 

3. 노력하지 않아도 성능이 개선된다.

 - 라이브러리 사용자가 많다면, 이를 만든 제작자들은 성능 개선을 위해 노력할 수 밖에 없다. 때론 성능이 극적으로 개선되기도 한다.

 

4. 기능이 점점 많아진다.

 - 실제로 커뮤니티에서 얘기가 나오고 논의된 후 다음 릴리스에 기능이 추가되기도 한단다.

 

5. 낯익은 코드가 된다.

 - 범용적으로 사용하는 라이브러리가 우리의 코드에 있다면, 다른 개발자도 읽기 쉽고, 유지보수하기 쉽다.

 

하지만 실상은 직접구현이 많다

대체할 수 있는 표준 라이브러리가 있음에도 직접 구현하는 개발자들이 많다고 한다. 이유는 대체할 수 있는 라이브러리가 있다는 사실을 모르기 때문이다.

메이저 릴리즈마다 주요 기능이 라이브러리에 추가되고 공시된다. 때문에 홈페이지에 직접 들어가 한번씩 읽어보는 것이 좋다.

 

지정한 URL 파싱

예를들어 지정한 URL의 내용을 가져오는 기능을 직접 구현하면 복잡하지만, 자바 9버전에 추가된 InputStream.transferTo 메서드를 사용하면 쉽게 구현할 수 있다.

 

public static void main(String[] args) throws IOException {
    try(InputStream in = new URL("https://www.naver.com/").openStream()){
        in.transferTo(System.out); // 콘솔에 파싱데이터가 출력됨.
    }
}

 

자바 프로그래머라면 적어도...

라이브러리가 너무 방대하여 모든 API 문서를 공부하기는 벅차겠지만, 이 책의 저자는 자바 프로그래머라면 적어도 java.lang, java.util. java.io 패키지들에는 익숙해져야 한다고 말하고 있다.

추가로 컬렉션 프레임워크나 스트림 라이브러리의 경우 유용하게 사용되고 동시성과 관련있는 부분도 있어 알아두는 것을 추천하고 있다.

 

정리

어떤 기능을 개발하기 전 라이브러리가 존재하는지 먼저 살펴보는 습관을 기르자. 그게 프로젝트만의 기능이 아니라면, 누군가는 똑같은 고민을 했을거고, 이를 구현했을 확률이 높다. 누군가 구현했다면 감사히 쓰면된다.

반응형
반응형

개요

 Git에는 local, remote 리포지토리와 같은 저장소도 있지만, commit 하기 전 상태를 저장해놓는 저장소도 있다. 이 저장소를 Staging Area라고 한다.

 

Commit 하기 전이요??

Commit 하기 전은 특정 파일을 add 명령어를 사용해 staged 상태로 변경시켰을때를 말한다. 즉, 어떤 파일을 새로 생성하거나, 수정한 후 add 명령어를 사용하면 이를 "staged 상태로 변경했다" 라고 말한다.

 

staged 상태가 있다면 다른 상태도 있나요?

그렇다 Staged 말고도 Untracked, Unmodified, Modified 상태도 존재한다. 이 상태들은 크게 두 가지로 구분할 수 있다. 깃이 관리하는 Tracked 상태(관리대상), 깃이 관리하지 않는 Untracked 상태(비관리 상태)이다. Tracked 상태에 Unmodified, Modified, Staged 상태가 포함된다. 아래 이미지를 보며 이해해보자.

 

출처 : https://velog.io/@soyi47/GitGithub-staging-commit

 

 

1. Untracked 상태

 깃이 관리하지 않는, 즉, 파일의 변경에 대해 추적하지 않는 상태이다. 이러한 상태를 갖는 파일들은 어떤 파일일까? 새로 생성한 파일의 경우 COMMIT 하기 전 특정 부분을 수정한다 한들 수정된 라인을 알 수 없다. 아직 깃에 의해 관리되지 않기 때문이다. 깃에 의해 관리되다가 삭제된 파일도 마찬가지이다. 이러한 상태를 Untracked 상태라고 한다.

 새로 생성한 파일을 add 하여 Staged 상태를 만들고, 이를 Commit 한다면, 이는 깃에 의해 관리(Tracked)되는 Unmodified 상태가 된다.

 

2. Unmodified 상태

 수정되지 않은 상태이다. 파일을 수정하지 않거나, 수정, 생성했던 파일을 commit 한 직후에 해당하는 상태이다.

 

3. Modified 상태

 수정된 상태이다. 단, 깃에 의해 관리되는 Staged 상태의 파일에 대해 수정을 했을 때이며, Untracked 상태의 파일을 수정했을 때는 Modified 상태라고 하지 않는다.

 

4. Staged 상태

  Commit 시 저장소에 기록될 준비를 마친 상태이다. 이를 "stage에 올라갔다" 라고도 표현한다.

 

Stage 에 올라간 파일들은 어디에 저장되나요?

.git/index 경로에 Stage에 올라간 파일들이 저장된다.

 

Stage 상태가 필요한가요? Commit 까지의 과정을 번거롭게 하는 것 같아요

이를 설명해주는 좋은 글이 있어 공유한다.

https://blog.npcode.com/2012/10/23/git%EC%9D%98-staging-area%EB%8A%94-%EC%96%B4%EB%96%A4-%EC%A0%90%EC%9D%B4-%EC%9C%A0%EC%9A%A9%ED%95%9C%EA%B0%80/

 

Git의 Staging Area는 어떤 점이 유용한가

Git에는 Staging Area라는 공간이 있다. 어떤 변경사항이 저장소에 커밋되기 전에, 반드시 거쳐야만 하는 중간단계이다. 다른 버전관리도구에는 이에 정확히 대응하는 것은 없다. 저장소가 추적하는

blog.npcode.com

 

이해한 내용을 정리하면 아래와 같다.

 

첫째, Commit 예정인 파일들을 관리할 수 있다.

사실 어떤 파일을 생성하거나 수정한 후 바로 Commit을 할 수도 있지만, 개발을 하면서 Commit 하고자 하는 파일들을 넣고 빼나가면서 '이번 Commit에는 최종적으로 어떤 파일들이 추가되어야 하는지'를 고민하면서 하나의 Commit 에도 공을 들이시는 경우도 있다. (참고로 필자는 아니다) 이 때 특정 파일들을 Stage 에 저장하며 관리한다면, Commit 예정인 파일들을 관리할 수 있다.

 

둘째, 충돌을 해결할 때 사용된다.

merge 시 충돌이 발생할 경우 이를 해결해야 한다. 만약 충돌의 범위가 거대하여 많은 시간이 소요된다면 중간 세이브 파일처럼 충돌을 해소한 파일을 디스크 어딘가에 저장해놓는 것이 안전하다. 이처럼 깃에서는 충돌을 해결한 부분에 대해 add 할 경우 해당 파일이 Stage 에 올라가 디스크에 저장된다.

 

참고

https://blog.npcode.com/2012/10/23/git%EC%9D%98-staging-area%EB%8A%94-%EC%96%B4%EB%96%A4-%EC%A0%90%EC%9D%B4-%EC%9C%A0%EC%9A%A9%ED%95%9C%EA%B0%80/

https://velog.io/@soyi47/GitGithub-staging-commit

반응형

+ Recent posts