IDE에서 GIT을 이용하다 보면 가끔 

 

파일 전체가 변경되었다고 표기되면서 변경점을 잡아내지 못하는 경우가 있습니다. 

 

혹은 PULL 받아온 파일에는 분명 변경된 점이 없는데, GIT에서 변경점을 잡아 오류처럼 보이기도 합니다. 

 

이는 CRLF설정 때문에 발생하는 현상입니다. 

 

 

CRLF 개행문자, 운영체제별로 각각 설정이 다르다 

 

CRLF는 띄어쓰기를 입력할 때 코드를 \r, 혹은 \n, 혹은 \r\n 어느 것으로 개행문자를 입력할 지를 구분짓는 설정입니다. 

 

운영체제별로 설정이 달라 Unix, Linux는 \n 

 

클래식 Mac Os ( 9버전 이하 ) 는 \r

 

Windows OS는 \r\n으로 표기합니다. 

 

이러한 차이점 때문에, OS와 상관없이 공통된 코드를 관리하는 시스템 ( IDE, GIT 등 ) 에서는 아무런 차이가 없는 파일처럼 보이더라도

 

띄어쓰기의 개행문자를 해석하는 과정에서 차이가 있는 것으로 인식할 수 있습니다. 

 

이러한 점을 고려하여서 여러 IDE에서는 CRLF 설정을 지원하고 있습니다. 

 

Intellij, 하단 사이드바 및 설정페이지에서 변경 가능하다.

 

 

인텔리제이는 공식 문서에서 친절하게 CRLF 설정과 바꾸는 법에 대해 설명해주고 있습니다. 

 

 

Configure line separators | IntelliJ IDEA

 

www.jetbrains.com

 

첫째로, 아래의 사이드바의 버튼을 통해서 바꾸는 방법이 있습니다.

 

아래 사이드바에서 간단하게 변경할 수 있다.

 

둘째로, 직접 설정페이지의 Code Style에서 바꾸는 방법이 있습니다. 

 

CodeStyle 항목의 Line Separator, 운영체제에 의존하도록 바꿀수도 있다.

 

Eclipse, WorkSpace 설정 및 EGit의 auto CRLF 설정이 있다.

 

이클립스 역시 WorkSpace에서 "New text file line delimiter를 통해서 os, 혹은 windows/ Unix로 바꿀 수 있습니다. 

 

다만 바꿀 시에 어떤 개행문자를 사용하는지 명확하게 나타나지는 않습니다. 

 

CR, LF, CRLF 어떤 개행문자를 사용하는지 명확하게 표기되지는 않는다.

 

Eclipse 내부에서 사용할 수 있는 e-git에서는 git에서 제공하는 auto CRLF 기능을 추가적으로 설정할 수 있는데, 

 

이는 "git config --global core.autocrlf true" 라고 하는 git의 기본 설정을 IDE 내부에서 설정으로 바꿀 수 있도록 

 

UI를 구현해주는 기능입니다. 

 

이를 true로 구현하면 window에서는 파일을 가져올 때  \r( LF ) 가 \n\r ( CRLF )로 바뀌게 되고 

 

PUSH할 때는  \n\r ( CRLF )가 \r( LF ) 로 변경됩니다. 

 

autocrlf 설정을 바꿀 수 있다.

 

이 설정은 초기에 True처리 되어있기 때문에, e-git을 사용할 때 만일 다른 사람이 CRLF로 개행한 코드를 가져오게 되면 

 

변경사항이 없음에도 파일 전체가 변경된 것으로 인식할 수 있으니 

 

파일에 변경사항이 없는데도 변경된 것 처럼 인식될 경우 관련설정을 false 처리 해보시기 바랍니다. 

 

 

 

VsCode, 하단 사이드바 및 설정페이지의 Eol 항목에서 변경가능하다. 

 

VsCode 역시 하단의 CRLF 항목으로 현재 적용되는 개행문자를 변경할 수 있습니다. 

 

Intellij와 같이 바로 보여준다.

 

더 자세히 변경하고 싶다면 설정에서 Eol을 입력하면 됩니다. 

 

auto로 os에 의존하도록 변경할 수 있다.

 

 

혹시 형상관리 툴을 사용할 때, 변경사항이 없는데도 문제가 발생한다면 

 

이처럼 IDE의 여러 개행문자 설정을 다시 한 번 점검해 보시길 바랍니다. 

 

 

 

Java 코드는 IDE에서 의존성을 나타내 주는 경우가 많지만 

 

Javascript 코드 혹은 Mapper에서 특정 함수나 테이블의 의존성을

 

"문자열 찾기" 없이 모두 검증하기는 어려울 것입니다. 

 

그래서 많은 분들이 개발, 혹은 결함처리 단계에서 한 파일단위,

 

혹은 전체 프로젝트 단위에서 검색기능을 활용하시는 것으로 알고 있습니다. 

 

이에 Eclipse나 Intellij 에서는 보다 정밀한 검색기능을 위해서

 

정규표현식(RegExp)으로 검색할 수 있는 기능을 제공하고 있습니다. 

Intellij 문자열 찾기 정규표현식 활성화
Eclipse 프로젝트 파일 찾기 정규표현식 활성화

 

이렇게 정규표현식을 잘 활용하여 원하는 결과를 찾는다면, 개발 시간의 획기적으로 단축될 수 있고 

 

IDE가 의존성 체크를 지원하지 않는 파일 형태라도 쉽게 의존성을 체크할 수 있습니다.

 

이런 정규표현식을 연습할 수 있는 사이트인 regexr.com을 소개합니다. 

 

 

RegExr: Learn, Build, & Test RegEx

RegExr is an online tool to learn, build, & test Regular Expressions (RegEx / RegExp).

regexr.com

 

이 사이트는 Expression, text, tools로  구성되어 있으며

 

별도의 인증정보 없이 손쉽게 웹에서 정규표현식을 연습하고 테스트할 수 있습니다. 

 

 

첫째로, 헷갈리기 쉬운 Expression Flag ( 대소문자, 전역, 라인단위, 유니코드 등을 의미 )  를

 

버튼형태로 자동완성하여 쉽게 실습할 수 있습니다. 

오른쪽의 Flag 버튼으로 자신의 expression에 Flag를 자동으로 추가한다.

 

 

둘째로, 정규표현식에 대한 Cheatsheet와 Document (Reference) 를 제공하여 기본 표현식을 쉽게 연습해 볼 수 있습니다. 

 

개별 정규표현식 단어, ^ 나 $, \b 등에 대한 사전이 정리되어 있다고 보시면 될 것 같습니다. 

 

Regex에 대한 간단한 설명 및 Document가 정리되어 있다.

 

셋째, 전방탐색과 후방탐색(lookaround)을 지원합니다.

 

일치영역을 발견하지만 값을 판단하지는 않는 전방탐색과 후방탐색을 이용하면

 

~앞에, ~뒤에 등의 단어를 쉽게 찾아볼 수 있습니다. 

 

전방탐색, 후방탐색에 대한 서치결과 및 어떤 방식으로 서치가 진행되는지를 보여주는 tools 창

 

마지막으로, 계정정보를 만들어 로그인하면 나만의 정규표현식을 저장하여 불러오기 할 수 있습니다. 

 

둘째로, 정규표현식에 대한 Cheatsheet와 Document (Reference) 를 제공하여 기본 표현식을 쉽게 연습해 볼 수 있습니다. 

 

 

개별 정규표현식 단어, ^ 나 $, \b 등에 대한 사전이 정리되어 있다고 보시면 될 것 같습니다. 

 

Community Pattern에는 RFC기반의 Email 정규표현식, 비밀번호 정규표현식, Ip 정규표현식 등 수많은 개발자들이 올려놓은 정규표현식 예시도 있으니 

 

 

이를 참고하시면 실력향상에 더욱 도움이 되실 수 있을 것이라 생각합니다. 

 

이렇게 Regexr을 사용하여 정규표현식을 연습해 보는 것을 추천드립니다. 

'정보게시판 > 사이트 공유' 카테고리의 다른 글

보안 취약점 정보 제공 사이트 CVE  (0) 2022.11.09
공부관련 사이트  (0) 2022.06.26

Service Provider Interface

 

SPI, 서비스 제공자 인터페이스 개념

 

 

 

Service Provider Interface는 Service Provider Framework의 한 구성요소입니다. 

 

Service Provider Framework는 확장 가능한 Java Application을 만드는 패턴 중 하나입니다.

 

외부에서 클라이언트에게 Jar파일을 받아, 기존 코드의 변경 없이 기능을 확장할 수 있습니다.

 

 

대표적으로 Java EE(Enterprize Edition)의 핵심 기술 중 하나인 JDBC가 이 패턴을 활용하고 있습니다. 

 

Java Database Connection

 

JDBC가 SPI로 구현된 덕분에

 

우리가 Java 코드로 Database와 Connection을 연결할 때에도

 

Oracle, Mysql, Mssql 등의 모든 드라이버 코드를 먼저 구현하는 것이 아니라, 

 

핵심적인 코드만 남겨두고 각각의 데이터베이스 회사에서 만든 JAR파일을 받아 넣어 사용할 수 있는 것입니다. 

 

기존의 Java 코드는 변화하지 않으면서, 클라이언트가 만든 파일을 통해서 기능을 확장시켜 구현할 수 있고, 

 

만약 클라이언트가 Driver를 넣지 않으면 기본적인 Driver 객체를 반환하여 연결할 수 도 있습니다. 

 

즉, 최소한의 조건 (SPI Interface의 구현) 만을 지키면 얼마든지 클라이언트가 새로운 객체를 만들어서 

 

자유롭게 데이터베이스 연결을 다룰 수 있는 것입니다. 

 

 

SPI의 필수요소 세 가지

 

SPI 를 구현하기 위해서는 핵심적으로 세 가지의 요소가 필요합니다.

 

Service, 변하지 않는 기능

 

먼저 기능을 수행하는 변하지 않는 코드가 필요합니다.

 

Service Provider Interface는 기능을 확장하기 위하여 사용하는 것이므로 

 

사용자가 아무런 값을 넣지 않아도 기본적으로 실행되는 기능이 존재해야 합니다. 

 

또, 이 기능은 외부 Resource(JAR파일) 에 의해서 바뀌지 않아야 합니다. 

 

Service Provider Interface (SPI),

확장기능에 필요한 최소한의 조건을 명시하는 인터페이스 

 

 

만약 기능을 확장하고 싶다면, 최소한 SPI로 명시된 인터페이스를 Implement하여 구현체를 만들고 

 

추상메서드를 오버라이드 하여 필수 조건을 충족시켜야 합니다. 

 

인터페이스를 통해서 구현하기 때문에, 인터페이스에 있는 최소한의 조건 ( 추상메서드 구현 ) 만 맞추면 

 

다른 코드의 작동과 상관없이 앞의 "Service( 변치 않는 기능 )" 을 사용할 수 있도록 구현해야 합니다. 

 

Service Provider, SPI의 구현체 

 

클라이언트가 SPI를 구현하여 확장시킨 SPI의 구현체입니다. 사용자가 자유롭게 커스터마이징하여 기존의 기능에서 

 

확장된 또다른 기능을 코드의 변경없이 제공할 수 있는 리소스입니다. 

 

 

 

 

 

SPI Tutorial Oracle Documentation

( 오라클 SPI 튜토리얼 문서 ) 

 

오라클에서 제공하는 사전을 만드는 예제를 통해서 SPI 패턴을 실습 해 봅시다. 

 

 

 

Creating Extensible Applications (The Java™ Tutorials > The Extension Mechanism > Creating and Using Exten

The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See Java Language Changes for a summary of updated

docs.oracle.com

 

위의 링크에 들어가서 DictionaryServiceDemo.zip 파일을 다운로드 받아 열어보면 다음과 같은 구조를 가집니다. 

 

DictionaryDemo, Ant 빌드 툴을 사용하여 모든 사전을 불러온 후, 검색기능을 사용합니다.

 

DictionaryServiceProvider, SPF의 기본 인터페이스와 클래스(Service, SPI)를 구성합니다.

 

ExtendedDictionary, SPI를 Implement하여 구현한 확장 사전입니다.

 

GeneralDictionary, SPI를 Implement하여 구현한 일반 사전입니다.

 

 

DictionarySerivce, Service 로직

 

DictionaryService 의 코드 구조를 보면, 핵심 로직은 단 하나, getDefinition(String word)라는 메서드로, 

 

Dictionary라는 클래스를 가진 ServiceLoader<Dictionary>를 호출하여 

 

Dictionary 클래스들을 돌아가면서 그 안의 getDefinition() 함수를 부르고, 

 

다음 사전이 있다면 같은 작업을 반복하는 것을 확인하실 수 있습니다.  

 

이 DictionaryService는 이 Service Provider Framework의 핵심 로직 중 하나로, 

 

어떤 외부 Dictionary ( 다른 사전, 즉 영어사전, 한문사전 등등.. ) 가 들어오건 간에  변형되지 않는 원본 코드입니다. 

 

public class DictionaryService {

    private static DictionaryServiceExample example;
    private ServiceLoader<Dictionary> loader = ServiceLoader.load(Dictionary.class);

		// 1. 생성자를 private 처리하여 일반 생성하지 못하도록 막는다. 
		// ** static synchronized를 이용하여 Singleton Pattern으로 하나의 객체만을 만든다.
    private DictionaryServiceExample() {}
    public static synchronized DictionaryServiceExample getInstance() {
        if (example == null) {
            example = new DictionaryServiceExample();
        }
        return example;
    }

    // 2. 모든 사전을 경유하며 단어를 찾는 핵심 서비스 로직이다.
    public String getDefinition(String word) {
        String definition = null;

        Dictionary d;
        try {
			// Dictionary 객체들을 경유하며 map에 특정 단어를 찾는다. 
            for(Iterator<Dictionary> dictionaries = this.loader.iterator(); definition == null && dictionaries.hasNext(); definition = d.getDefinition(word)) {
                d = (Dictionary)dictionaries.next();
            }
        } catch (ServiceConfigurationError var5) {
            definition = null;
            var5.printStackTrace();
        }
        return definition;
    }
}

 

 

 

Dictionary, 서비스 제공 인터페이스 (SPI) 

 

 

 

Dictionary는 클라이언트가 새로운 사전 ( 확장된 기능 ) 을 만들기 위해 반드시 구현해야 하는 인터페이스입니다. 

 

추상클래스 (Abstract Class) 로 getDefinition()이라는 메서드가 선언되어 있습니다. 

 

이 Dictionary를 implement 한다면 getDefinition() 메서드를 강제로 오버라이드 해야합니다. 

 

오버라이드 된 메서드를 구현한다면, 위의 "사전 탐색 서비스", 

 

Service 를 이용할 최소한의 조건을 충족한 것입니다. 

 

사용자는 복잡한 원본 코드 ( DictionaryService ) 를 신경 쓸 필요 없이

 

필요한 사전에 대한 인터페이스를 구현하기만 하면  얼마든지 자신이 원하는대로 사전을 추가할 수 있습니다. 

 

public interface Dictionary {
    public String getDefinition(String word);
}

 

 

 

ExtendedDictionary, 사용자가 추가로 구현한 SPI의 구현체 Service Provider 

 

 

 

아래의 코드처럼 확장 사전을 만드려면, Dictionary(SPI)를 구현한 다음 

 

강제 오버라이드 된 getDefinition만 구현해 주면 됩니다. 

 

 

//---------------------------- 확장 사전 ---------------------------------
public class ExtendedDictionary implements Dictionary {

        private SortedMap<String, String> map;

    // Map을 활용하여 단어를 넣는다. 
    public ExtendedDictionaryExample() {
        map = new TreeMap<String, String>();
        map.put(
            "xml",
            "a document standard often used in web services, among other " +
                "things");
        map.put(
            "REST",
            "an architecture style for creating, reading, updating, " +
                "and deleting data that attempts to use the common " +
                "vocabulary of the HTTP protocol; Representational State " +
                "Transfer");
    }

    @Override
    public String getDefinition(String word) {
        return map.get(word);
    }
}

 

 

Dynamic ClassLoading, Java 고유의 동적 컴파일기능 

 

 

이제, 이 SPI를 통해서 사용자가 자유롭게 기능을 확장시킬 준비가 거의 끝났습니다. 

 

그런데, 외부에 있는 Jar파일을 이미 컴파일된 자바 언어가 어떤 방식으로 다시 읽어들이는 걸까요? 

 

사실 이 ExtendedDictionary는 지금의 코드와 같은 형식이 아니라, 

 

JAR파일 ( Java 압축파일 ) 로 사용자가 폴더에 넣어주기 때문에 

 

현재 JVM은 실제로 이 클래스의 존재를 모릅니다. 

 

그러나 Java는 어플리케이션이 가동되는 순간에도 모든 상속관계를 점검해서 

 

해당 클래스를 상속받는 클래스들을 다시 컴파일 할 수 있습니다. 

 

이를 Dynamic ClassLoading 기술이라고 하며, Java의 ClassLoader라는 클래스가 제공하는 기능입니다. 

 

이를 통해서 우리는 사용자가 새로 만든 전혀 새로운 클래스라도 다시 컴파일 할 수 있습니다. 

 

다만, 컴파일 해야하는 경로를 명시해 주어야만이 ClassLoader가 이를 찾아갈 수 있습니다. 

 

 

META-INF에 이름을 해당 클래스로 명시한 후, 

 

ServiceLoader에서 현재 로드하고 싶은 SPI (Dictionary) 를 상속한다면 확장 사전 ( SP )가 동적으로 

 

어플리케이션 실행 도중 로드될 수 있는 모든 조건이 갖추어집니다. 

 

 

public class DictionaryServiceExample {

    private static DictionaryServiceExample example;
    
    // ServiceLoader가 Dictionary라는 클래스를 상속받는 클래스를 찾는다. 
   	// .load가 끝나면 사용자가 넣은 새로운 Dictionary (ExtendedDictionary)가 추가된다. 
    
    // ** 이때 내부적으로  META-INF에 있는 파일 이름을 찾아서 해당 경로의 클래스를 로드한다.
    private ServiceLoader<Dictionary> loader = ServiceLoader.load(Dictionary.class);

    private DictionaryServiceExample() {}
    public static synchronized DictionaryServiceExample getInstance() {
        ...// 생략 
    }

    // 2. 모든 사전을 경유하며 단어를 찾는 핵심 서비스 로직이다.
    public String getDefinition(String word) {
        ...// 생략
    }
}

 

 

실제 코드 실행 

 

이제, 이 SPI를 통해서 사용자가 자유롭게 기능을 확장시킬 준비가 거의 끝났습니다. 

 

이제 DictionaryDemo 클래스에서 main 메서드를 통해 실제 사전탐색을 실시해 봅시다. 

 

Jar 파일로 빌드된 ExtendedDictionary의 xml, rest 를 성공적으로 찾아 반환한다면 

 

기존의 코드인 DictionaryService의 변형 없이, 새로운 클래스 코드의 추가 없이, 

 

사용자가 건네준 ExtendedDictionary라는 클래스 구현과 활용에 성공한 것입니다. 

 

public class DictionaryDemo {
    public DictionaryDemo() {
    }

    public void main(String[] args) {
        DictionaryServiceExample dictionary = DictionaryServiceExample.getInstance();
        System.out.println(lookup(dictionary, "xml"));
        System.out.println(lookup(dictionary, "REST"));
    }

    public String lookup(DictionaryServiceExample dictionary, String word) {
        String outputString = word + ": ";
        String definition = dictionary.getDefinition(word);
        return definition == null ? outputString + "Cannot find definition for this word." : outputString + definition;
    }
}

 

 

결론, SPI는 자바의 핵심 기술인 Dynamic ClassLoading 의 실제적 활용이다. 

 

 

이처럼 Java는 런타임 도중에도 클래스를 동적으로 로딩함으로써 

 

클라이언트가 어플리케이션 운영 도중 삽입한 자바 코드에 대해서도 

 

유연하게 바로 반영할 수 있는 장점을 지니고 있습니다. 

 

SPI는 Java의 이러한 Dynamic ClassLoading을 활용한 기술이며 

 

서비스 규모가 확장되어야 할 때 아주 유용한 설계 패턴 중의 하나입니다. 

 

 

기회가 되신다면 소개해 드린 Oracle Tutorial을 직접 따라하시면서 

 

이러한 자바의 강력한 런타임 동적 클래스로딩 기능을 실습해보시기 바랍니다. 

 

다음 글에서는 이러한 Service Provider Framework가 

 

JDBC에서 어떻게 작동하는지를 작성해보도록 하겠습니다. 

 

 

'플라자' 카테고리의 다른 글

JAVA의 객채지향 - 상속 관계  (0) 2022.06.30
JAVA의 객채지향  (0) 2022.06.12

세상에 수많은 OS들이 존재한다. 대표적으로는 윈도우가 먼저 떠오르는 사람이 있을 수 있고 IOS가 떠오르는 사람도 있을 것이다. OS란 녀석들이 하드웨어에 탑재됨으로써 하드웨어는 하나의 역할이 아닌 다양한 역할을 수행할 수 있게 된다. 또한 OS 특성에 따라 여러 가지의 작업을 동시에 처리도 가능하다.

 

그리고 많은 OS중에서 여기서는 리눅스라는 녀석에 대해서 이야기를 간단하게 해 보자.

 

리눅스란?

리눅스의 마스코트는 펭귄이다.

리눅스는 오픈소스 운영 체제로써 누구나 무료로 사용을 할 수 있다는 것이 큰 장점이다. 그리고 타 OS에 비해서 상대적으로 뛰어난 보안과 안정성으로 서버단에서 많이 채택을 하고 있다. 리눅스의 종류도 여러 가지가 있지만 여기서는 구체적으로 언급하지 않고 넘어간다.

그 외에 리눅스의 역사와 성능 등 할 이야기는 많지만.. 더 전문적인 웹사이트를 보는 것이 바람직한 선택일 것이다.

여기서부터는 윈도우10을 기준으로 작성이 되었습니다.

 

본격적으로 리눅스를 시작해보자! 자신의 컴퓨터가 윈도우 10 이상이며 2004 버전까지 업데이트를 완료했다면 윈도우에서 제공하는 WSL(Windows Subsystem for Linux)를 이용해서 자신의  윈도우에 리눅스를 설치할 수 있다!

여기서는 스토어에서 간단하게 설치를 하였다. 해당 버전말고 원하는 버전을 설치해도 무방하다.

자세한 설치방법은 여기서는 생략을 하도록 하였다.

 

정상적으로 리눅스가 설치가 되었다면 윈도우에서도 리눅스를 편안하게 즐길 수 있다.

 

우선 리눅스를 처음 접한다면 많이 당황할 수 있다. 이상한 글자들만 눈앞에 보이고 아이콘은 전혀 보이지 않기 때문이다.

 

평소에 접하던 윈도우 또는 맥 OS 같은 경우는 모든 사용자가 처음 접했을 때 조금이라도 더 직관적으로 접할 수 있도록 GUI 스타일을 채택하였지만 리눅스에서는 CUI 스타일을 고수하기 때문이다. 물론 리눅스에서도 GUI 스타일을 채택을 한 것도 있지만 대부분 서버용으로 사용하는 리눅스는 CUI 스타일을 사용한다.

 

처음으로는 낯선 곳에 도착을 했으니 둘러보는 것이 우선이라고 생각한다. 지금 보고 있는 화면에서는 마우스를 통해서 이동한다는 개념이 없다.  즉 모든 행동을 직접 입력해야 한다.

 

밑의 사진처럼 디렉토리를 하나 이동할 때에도 직접 입력을 해야 한다.

사진에서는 '/' 라는 디렉토리로 이동을 한 것이다.


처음 접하게 되면 많이 복잡하고 낯설어서 피하게 되지만 하다 보면 나름의 매력?도 존재하는 방식이다.

처음부터 한 번에 다 배울 생각은 절대로 하지 말고 꾸준히 하나씩 학습을 해보자.

+ Recent posts