MySQL에서 인코딩을 설정할 때 Collation이라는 개념이 나오는데 이 점은 Character set과는 조금 차이를 보인다.


 Character Set – 특정 문자가 저장될 때 어떻게 Encoding되어 저장될 것인지 규칙이 저장되어 있는 집합 (ASCII, EUC-KR, UTF8 등)


 Collation – 특정 문자셋에 의해 DB에 저장된 값들을 검색하거나 정렬 등 작업을 위해 문자들 간에 ‘비교’ 시에 사용하는 규칙들의 집합.


 이 설정은 일반 type이 아닌 char, varchar 등 데이터 타입을 갖는 칼럼에 적용한다.

Binary 방식(대소문자 구별) 이나 Case Insentive(대소문자 미구별) 방식이 있으며 Case Insentive 방식의 경우 뒤에 ci가 붙는다.


 자주 접하게 되는 내용은 아니지만, 맨처음 MySQL / RDS 를 세팅할 시, 이 개념을 잘 이해하지 못하고 한쪽만 설정해서 컨텐츠의 Encoding 이 깨졌었던 이슈가 있었다. 설정 시에 양쪽 모두 확인하고 알맞게 세팅하는 것이 중요하다.


(참고 linuxism.tistory.com/432)




 실무에서 REST API 설계를 처음 다뤄보았을 때, 부족한 점들이 많이 있었다. 

많이 혼나고 나서 너무나도 좋은 글을 찾아 정리해두었다.


원본 출처는 다음과 같으며, 아래 내용은 한글로 알아보기 쉽게 정리해 놓은 부분이다.

(https://blog.mwaysolutions.com/2014/06/05/10-best-practices-for-better-restful-api/)



 RESTful Service API 의 설계 업무를 하던 중 처음에 칭찬받아서 대충 설계하다가 다시 공부하게 된 내용.

일반적으로 커뮤니케이션과 개발에 원활한 설계를 하기 위한 10가지의 방법이 존재하며 다음과 같이 서술된다.


(1) Use nouns but no verbs (동사가 아닌 명사를 통한 API 설계를 할것.)


- 가장 초보적인 실수가 CRUD 에 대한 기능을 Path에 명시하는 것이다.

 예를들어 /getAllCars, /createNewCar, /deleteAllCars 와 같은 방식인데, REST 디자인을 위해서는 CRUD 를 Method 를 이용하며 표현하기 때문에 그 외의 것은 전부 entity를 명시하는 명사로 표기한다.


 따라서 예시는 다음과 같이 HttpRequest Method를 통해서 기능이 구분되어야 한다.




 이 원칙은 표준 웹의 REST API 대부분에서 따르는 원칙이며, GET / POST / PUT / DELETE 외의 Method 들, 가령 HEAD 나 TRACE 등, 의 경우 별도의 원칙을 갖고있지 않다.



(2) GET method and query parameters should not alter the state


 역시 위에 명시한 바와 마찬가지로 모든 Functioning 은 Method로 하고 query parameter 역시 state change 등의 기능으로 사용하지 않는다.

(ex) GET /users/811?activate or GET /users/811/activate ---         (X)



(3) Use plural nouns


 path는 entity 항목을 나타내므로 복수형을 쓰고 특정하게 지정하고 싶다면 다음 Path에서 {항목} 으로 지정해준다.

(ex) GET /cars/{carName}            -- cars 와 같이 path 는 복수형. 뒤에 {carName} 으로 단수 객체 모델을 가져온다.



(4) Use sub-resources for relations


 리소스는 관계에 따른 계층형 구조를 유지한다.


(ex) GET /cars/811/drivers/ > 차811 의 드라이버 리스트를 반환한다.

(ex) GET /cars/811/drivers/4 > 차 811의 4번 드라이버 정보를 반환.



(5) Use HTTP headers for serialization formats


 서버와 클라이언트 간 커뮤니케이션에 있어서 Header Serialization format을 이용한다.



(6) Use HATEOAS


 HATEOAS 란 Hypermedia As The Engine Of Application State 의 약자로, 하이퍼텍스트 링크를 데이터 내에 명시할 경우 API를 통해 갈 수 있는 navigator 형태로 표기해준다.


<HATEOAS 의 예시. links 하위의 href 를 주목하자>


 위의 예시에서 drivers.links.href 항목은 주소를 저장함으로써, 외부주소에 저장된 리소스 자체를 가리키고 있다.

REST API 는 이런식으로 실제 리소스를 담는 것이 아닌 경로를 담아서 HyperMedia 컨텐츠를 기록한다.


 이렇게하면 네트워크 비용도 절감되고, 성능면에서도 많은 이점을 갖는다. 물론, 해당 주소로 다시 접근하여 실제 리소스를 받아와야 하는 점이 있지만, 오히려 리소스를 CDN 등을 이용한 static 저장소에 두고 가져온다면 네트워크 비용 측면에서도 훨씬 저렴하고 뛰어난 성능으로 핸들링할 수 있다.



(7) Provide filtering, sorting, field selection and paging for collections


 Filtering 은 특수한 query parameter 를 조건으로 주었을 때 해당 기능을 제공할 수 있게끔 하는 것이다.

(ex) GET /cars?color=red


 Sorting 은 오름차순, 내림차순의 정렬을 multiple field 에 대해 허용하는 것이다. 

(ex) GET /cars?sort=-name, +price > -(ascending) +(descending)


 Field selection 은 select 조건문과 같이 필드만 추출해서 보여줄 수 있도록 하는것으로, 네트워크 트래픽 관리에 효과적이다. 

(ex) GET /cars?fields=name, price


 Paging 은 Offset과 Limit 을 지정할 수 있는 조건으로 DB 조회에 필수적인 기능이다. (ex) GET /cars?offset=10&limit=5



(8) Version your API


 REST API는 Version 을 명기한다. (ex) /blog/api/v1



(9) Handle Errors with HTTP status codes


 에러는 별도의 로직으로 처리한다기 보다 HTTP 상태 코드와 메시지를 통해 처리하는 것이 좋다. 다음은 서버에서 반환하는 표준 HTTP 응답 코드를 설명한다.



에러에 따른 Payload 도 다음과 같이 명시한다.




(10) Allow overriding HTTP method


 몇몇 프록시 서버의 경우 POST와 GET 만을 지원하는 경우가 있다.

이러한 한계점을 지원하기 위해 API 단에서 override HTTP method 를 지원해줄 필요가 있다. X-HTTP-Method-Override 와 같은 Custom HTTP 헤더를 만들어 method 방식을 지원하자.






 마틴 파울러가 재창한 두개의 개념으로, 비즈니스 로직을 처리하는 두가지 패턴을 말한다.

이는 책임지는 쪽이 Domain Level이냐 Script Level이냐에 따라 구분된다. 가령 다음과 같은 예시가 있다고 가정해보자.


먼저 트랜잭션 스크립트란, 은행의 계좌 이체 서비스처럼 하나의 트랜잭션 안에서 필요한 모든 로직을 수행하는 패턴이다. 

그래서 이름도 Transaction Script 로 부른다. 구현이 매우 쉽고 단순하지만 구조가 복잡해질 수록 모듈화의 복잡도 역시 높아진다.

다만, 하나의 강건한 로직이 해당 모듈에서만 구현되어야할 경우 side effect 를 예방할 수 있고, 좀 더 효율적인 코드를 작성할 수 있다.


도메인 모델은 우리가 흔히 객체 지향의 예제로 많이 배우는 형태의 모델로, 각 객체에 객체가 수행해야 하는 업무를 분담시키는 것이다. 

주요 특징은 데이터와 프로세스가 같이 존재한다는 점이며, 객체간 관계를 맺을 수 있어, 제약하거나 로직의 단순화에 도움이 된다.


또한 주로 같이 사용되는 상속이나 다양한 패턴들이 객체를 중심으로 얽히면서 개발자가 선택할 수 있는 유연성이 높아지고 모듈화가 간단해진다. 유지보수가 편리하며 재사용성이 뛰어나다.


 하지만 객체들간의 설계가 수반되어야하기 때문에 모델 구축이 쉽지 않고, 객체간의 관계를 잘 풀어나가야 한다. 특히 객체간의 dependency 는 반대로 말하자면 제약사항이기 때문에 설계가 매우 중요하다.


도메인 모델과 트랜잭션 스크립트 패턴의 차이를 보기 위해 다음 예제를 확인해보자.



    Emp emp = loadEmp("나직원");
    Dept dept = loadDept("A부서");



이 때 이를 비교 하기 위한 Domain Model 방식과 Transaction Script 방식의 차이는 다음과 같다.



    //Transaction Script 방식
    if(emp.getDeptName() == dept.getDeptName())
        return true;

    //Domain Model 방식
    if(emp.isBelongTo(dept))
        return true;



즉, Function의 책임 소재를 어디에 두는가, 사용자가 작성하는 코드 로직에 두는가(Transaction Script 방식) 아니면 객체 자체의 모델링 자체에 두는가(Domain Model 방식)의 차이이다.


참조

http://javacan.tistory.com/entry/94

http://lorenzo-dee.blogspot.com/2014/06/quantifying-domain-model-vs-transaction-script.html




 Java8에서는 Lambda 식과 같은 함수형 프로그래밍을 지원하기 위한 API를 포함해서 이를 위한 특징적인 API 들이 많이 생겨났다.

그 중에서도 이 포스팅에서는 핵심적인 3가지, Stream, Optional, Lambda 에 대해 정리하고자 한다.


 먼저 컬렉션을 다루기위한 API 중에는 Stream API 가 존재한다.

Java 에서 컬렉션은 데이터를 관리하고 제어하기 위한 좋은 컨테이너이지만 구현의 인터페이스 적인 측면에 있어서는 깔끔하다고 보기는 힘들다.


특히 이것은 SQL 문과 비교해보면 이해가 쉽다. 학생 정보를 담고 있는 데이터 컬렉션에서 특정 성적 이상을 가진 학생들을 찾을 때 Collection을 이용하면 다음과 같이 구현할 수 있다.


List<Student> students = getStudentList();
List<Student> List = new ArrayList();
For(Student s : students)
{
    If(s.getGrade() >= 80)
        List.add(s);
}


반면, Sql 문을 사용한다면 이는 조건절이 포함된 SELECT 구문 하나로 처리가 가능하다.

Java8의 Stream API는 이러한 복잡한 구조의 데이터 처리를 간단하게 해주어 복잡도를 낮추는 설계가 가능하게 한다. 또한 이런 종류의 Lazy collection 은 멀티 스레드 및 병렬처리 환경에서도 개발자에게 상당히 유연한 선택지를 갖게 한다. 또한 한번만 소비 가능한 형태의 컬렉션이기 때문에 메모리 관리 차원에서의 이점도 있다.



List<Student> list = students.stream().filter(t->t.getGrade()>=80).collect(Collectors.toList());


Stream API 는 위와 같이 Collection 의 각 요소들을 따로따로 주어진 함수구문(위에서는 람다식)에 따라 처리하며 내부 버퍼를 통해 주어진 형태로 반환해준다.


Stream API 에는 위에 적용된 filter 나 collector 와 같은 메서드를 포함해서, map, flatMap, reduce, peek 등 다양한 메서드를 제공한다.

Stream API 의 자세한 사용에 있어서는 추가로 포스팅하고자 한다.


 새로 추가된 Optional API 는 Java8 에서 제공하는 Null이 포함되지 않는 Collection이다.

NullPointerException 을 걱정하지 않아도 되며, ifpresent(함수식) 기능을 통해 무결성 검증과 동시에 함수식을 수행할 수도 있다. 

가령 NULL 이 될 수 있는 객체에 대해 다음과 같이 핸들링 할 수 있다. 



public Student getStudentById(String studentId) {
// Stream API 의 findFirst() 는 그 자체로 Optional 을 return 하기 때문에 사실 이는 좀 어색한 함수 구현이다.
return getStudentList().stream()
.filter(student->student.getStudentId().equals(studentId)).findFirst().get();
}

public void foo() {

Student student = getStudentById("철수");

if(Optional.ofNullable(student).isPresent()) {
System.out.println("철수라는 학생이 존재합니다.");
} else {
System.out.println("철수라는 학생은 없습니다.");
}
}


위와 같이 Optional 을 사용하면 Null 을 핸들링할 수 있는 객체를 다룰 수 있다. if(student == null) 과 같은 형태 대신 좀 더 직관적으로 다양한 이용이 가능하다.


마지막으로 Java8의 람다식은 함수형 인터페이스를 제공함으로써 지원이 된다.

 함수형 인터페이스를 만드는 방법은 @FunctionalInterface 어노테이션을 이용해 명시적으로 생성도 가능하지만, method가 하나 존재하는 인터페이스를 선언함으로써 생성이 가능하다.

다음의 예시를 참조할 수 있다.



Public interface Foo
{
    Int calc(int a, int b);
}
{
    Foo add = (int a, int b) -> { return a+b; };
    Foo minus = (int a, int b) -> { return a-b; };
    Int addv = add.calc(3, 5);      //8
    Int minusv = minus.calc(5, 3);  //2
}


위의 예시는 직접 FunctionalInterface 를 구현하여 Lambda 식을 적용해본 것이고, 실제 많이 사용되는 것은 Java8 에서 제공하는 API 들이다. 다음은 그 몇가지 종류이다.


Function<T, R> : T 타입의 입력파라미터를 받아 R 타입을 리턴한다.


Supplier<T> : void 타입의 파라미터를 입력받아 T 타입을 리턴한다.


Consumer<T> : T 타입의 파라미터를 받아 void 를 리턴한다.


Predicate<T> : T 타입의 입력을 받아 boolean 을 리턴한다.


BiPredicate<T, U> : T와 U 타입을 입력받아 boolean 을 리턴한다.


UnaryOperator<T, T> : T 타입 2개를 입력받아 T 타입을 리턴한다.


이 외에도 많은 종류의 FunctionalInterface 를 제공하며, 잘 사용하면 함수형 프로그래밍의 장점을 누릴 수 있다.




 Grep 은 워낙 유명한 리눅스의 패키지이며 그만큼 쓸일이 많으므로 다음의 기본 사용법 정도는 꼭 알아두도록 하자.


 Grep 명령어는 파일 전체를 뒤져서 정규표현식에 해당하는 모든 행들을 출력하는 기능을 한다.


(ex) grep nw datafile -> datafile에서 nw를 포함하는 행 검색


grep nw d* -> d로 시작하는 모든 파일에서 nw를 포함하는 행을 검색


grep <keyword> * -> 모든 파일에서 키워드를 검색


grep '^n' datafile -> n으로 시작하는 datafile 내의 모든 행을 출력


grep '4$' datafile -> 4로 끝나는 datafile 내의 모든 행을 출력


grep '[^0-9]' datafile -> datafile 내에서 숫자가 아닌 문자를 하나라도 포함하는 모든 행을 출력


grep '[a-z]{9}' datafile -> datafile 내에서 소문자가 9번 이상 반복되는 문자열을 포함하는 모든 행을 출력한다.



 grep 실행시 다음 옵션을 부여함으로써 유용하게 정보를 가공할 수도 있다.


-r(하위 디렉토리를 모두 검사)

 

-n(행 번호를 함께 출력)


-i(대소문자 미구분)


-l(행 대신 파일명만 출력)


-w(정확히 일치하는 경우만 검색)


-v(포함되지 않은 행을 출력)


주로 사용하는 건 위의 옵션들이다. 더 많은 옵션은 다음 링크를 참조하자. 예제도 아주 잘 정리되어있다.

(https://en.wikibooks.org/wiki/Grep)


(ex) grep -v 'ffl' datafile > edit

// datafile에서 ffl이 있는 행만을 추출하여 edit 파일에 씀.


(ex) grep -rl "*print$"

// 현재 폴더를 루트로 하여 서브 디렉토리를 모두 검사하여 print 로 끝나는 행을 가진 파일의 이름을 출력.


 특히 서버에서 로그 분석 작업을 할 때, Grep 은 tail 과 함께 밥먹듯이 사용한다.

어느정도 프로젝트에서 자주 사용하는 정규식 포맷을 메모하여 grep 사용 시 적용시키는게 유용하다.






 Java에서는 Static 변수의 Non static 초기화를 위해 Static Initializer를 지원한다.

이는 static final 변수를 주로 예외를 throw하는 함수의 return 값으로 초기화하는데 사용된다.


다음과 같이 사용하면 된다.


(ex)



public class Example {

private static final Lib val;

static{
Lib tmp = null;
try{
tmp = new Lib();
}catch(UnknownHostException e){
//생성자가 이 예외를 반환하므로 일반적인 방법으로는 선언과 동시에 초기화가 불가능하다.
}
}

}



 이는 Lazy Initialization 의 예시로, 이러한 테크닉을 이용하면 Lazy Instantiation 과 유사하게 디자인 패턴 내의 코드를 작업하는 데 있어서 유연성을 가져올 수 있다.


 위의 예제에서 val 이라는 멤버변수는 final 인데다가 static 변수이면서 유효한 Lib 형의 객체이다. 안전하면서도 효율적이다.

위의 코드는 심지어 Thread-safe 하기 때문에 멀티 스레드 환경에서도 문제 없다.

 또한 위의 주석에 설명이 달려있듯, 생성자 자체가 예외를 발생시키는 경우 위와 같이 바깥에서 Try Catch 로 감싸줌으로써 안전한 초기화가 가능하다.


 다만, 이런 형태에서 위의 변수 val, 즉, Static Initialized Variable 은 클래스로더에 종속되기 때문에, 매번 객체 생성 시, 서로 다른 클래스 로더에서 Static 코드 블록을 호출하게 된다. 


 만약 하고자하는 바가 싱글턴이거나, 외부 코드에 종속적이라면 추천되지 않는 패턴이다. (물론 그렇게 쓰이는 경우는 흔치 않다.)





 Primary key(이하 PK)는 한 개 혹은 여러 개의 칼럼으로 테이블 내의 각 행들을 구별하기 위한 목적을 갖고 있다.

 PK는 그 자체만으로 Unique 하며 만약 여러 개의 Column이 PK로 묶여 있다면, 해당 값들의 조합이 반드시 Unique 해지게 된다.

 PK는 NULL 값을 갖을 수 없고(NOT NULL), Unique key라는 특징을 갖게 된다. 단순하게는 NOT NULL & UNIQUE == PK 라고 생각하면 편하다. 물론 좀더 디테일하게 보자면 차이가 있다.


 MySQL에서는 Int 작업이 빠르기 때문에 PK는 Int형으로 지정하는 것이 좋다. 


 Unique Key(Unique index)는 value의 값을 유일하게 만드는 column의 제약으로 PK와는 조금 다르게 NULL값이 가능하며, 역시 여러 개로 묶어서 Combination Index 를 만드는 것이 가능하다.


 * MySQL에서 Key와 Index는 동의어이다. (PRIMARY KEY 가 아니라 Key 가 Index와 동의어이다. 물론 PK 는 Index로 취급되긴 한다.)

 Unique Key 와 PK 의 차이점은, NULL 허용 여부와 의미 상의 차이가 있다. PK 로 지정된 Column 들은 Table 의 Row 를 구분하는 대표 값들이라는 Identity 를 갖지만, Unique Key 는 그보다는 좀더 제약(Constraint) 에 가깝다. 물론 "Unique" 해야 하기 때문에 구분하는 대표값으로 사용해도 무방하지만, PK 처럼 테이블의 대표값라고 취급하는 것은 위험하다.


 MySQL PK 지정 예시. (생성시)


(ex) Create table t(

id int not null, 

name char(10) not null, 

contents text, 

primary key(id, name));


 MySQL PK 추가 예시. (테이블에 추가)


(ex) Alter table 테이블 Add Primary key(칼럼)


 Key 값 조회하는 방법 예시.


(ex) Show Keys from 테이블



 물론 복잡한 DB 쿼리를 다루었던 프로젝트에서는 데이터 무결성을 효율적으로 관리하던 Constraint 를 본적이 있었지만, 경험상 실무에서 Unique Key Constraint 보다는 주로 PK로 값 연결하는 일이 많았다. 

 Unique Key Constraint 는 데이터 무결성에 있어서 좋은 도구이기는 하나... Constraint 도구들이 실 서비스에서도 유용하다고 볼순 없기 때문에 PK 만큼 유용하게 사용되지는 않는 듯 하다.






 리눅스에서 Java를 백그라운드 프로세스로 실행하려 할 때 다음과 같이 뒤에 &를 붙여주면 된다. 

(사실 리눅스에서는 자바 뿐 아니라 프로세스 실행 시 &만 붙여주면 백그라운드로 실행된다.)


 $ java -jar {run.java} &


- 단, 위와 같이 백그라운드 프로세스는 터미널이 필요하기 때문에(터미널에 종속되는 것이다.) 터미널 종료시, 로그아웃 시 프로세스도 같이 종료된다.


 이를 지원하기 위한 명령어로 nohup 명령어가 있다.

 이를 이용하면 리눅스에서 SIGHUP을 받지 않는 프로세스를 만들 수 있다.

 터미널이 종료될 때 프로세스가 죽는 이유는 해당 프로세스가 쉘의 자식 프로세스로 지정되어 있기 때문이다.

 부모 프로세스가 죽을 때 던지는 SIGHUP 을 자식 프로세스가 받게 되어 죽게 되고, 이를 무시할 수 있는 nohup을 이용하여 죽지 않게 만들 수 있다.


$ nohup java 클래스명&


 이는 사용이 편리하나 중지, 재시작 등 서비스가 일반적으로 갖고 있는 제어 루틴을 갖지 못하기 때문에 외부 명령어로 Kill 등을 해야 한다. 물론 Kill을 할 때 프로세스의 동작 제어는 많은 예외처리를 요구하게 된다.


 Java의 JVM은 UNIX 계열의 시그널 처리를 할 수 없기 때문에 따로 처리를 해줘야 하는데 이러한 번거로움을 Apache Commons Daemon을 이용하면 쉽게 해결할 수 있다.

 Jsvc는 중지 및 제어 신호에 대해 지정된 메서드를 실행할 수 있게 해주는 인터페이스를 갖고 있다. 


 Jsvc를 이용하려면 Daemon 인터페이스를 구현해야 한다. 또 쓰레드로 실행되지 않을 시 종료처리가 원만하게 되지 않기 때문에 143 오류가 발생하므로 Runnable 형태로 구현해야 한다.


 인터페이스를 구현하면 Jsvc를 이용하여 실행이 가능하지만 경로 입력이 힘들고 커맨드로 Start / Stop / Restart 로직의 제어가 힘들기 때문에 일반적으로 쉘스크립트를 작성한다.


 스크립트에는 Jsvc 커맨드를 주어 실행시키고 .jar 파일을 연결하여 Parameter로 전달해주도록 한다. 자세한 스크립트는 다음 링크를 참조한다. (win100.tistory.com/120)


 명령어 설정을 이용하면 로그의 Redirection이 가능하다. 터미널이 없으므로 주로 파일로 redirect 한다.




 실무를 하다보면 프로젝트에 따라 배치 스크립트를 작성할 일이 종종 있다.

그럴 때 듣게 되는 Daemon Process 라는 단어가 있는데, Background Process와 유사하면서도 의미가 조금 달라 헷갈리기 쉽다.


 Daemon Process 와 Background Process 모두 사용자와 상호작용하는 Foreground가 아닌 Background에서 동작하지만 둘 사이에는 차이가 있다. 


 일반적인 데몬은 터미널을 갖지 않는다. (유저와 상호작용하지 않는다.) 정확히는 데몬은 그 자체로 Process Group Leader로 부모가 1번 pid를 갖는 init으로 세팅된다. 즉, 독자적인 Session 을 갖고있으며 Linux System 자체가 아닌 별도의 독립된 Parent process를 갖지 않는다. 리눅스의 cron, smartd 와 같은 스케줄링 & 모니터링 프로세스들은 바로 이 Daemon Process 의 형태를 갖는다.


 백그라운드 프로세스는 이와 다르게 터미널을 통해 상호작용이 가능하다. 별도의 Parent process를 가질 수 있으며 Parent process와 세션이 공유되기 때문에 Parent process가 받는 Signal 의 영향을 받는다. (부모 프로세스가 종료되면 같이 종료된다.)



 Daemon Process의 조건 3가지는 다음과 같다.

(1) Fork로 자식 프로세스 생성 후 부모 프로세스를 종료 – 자식 프로세스의 ppid(소유 프로세스)를 pid 가 1인 init으로 양도


(2) Setsid를 이용하여 새로운 세션을 만들고 생성된 자식의 pid가 세션의 제어권을 가지도록 한다. – 세션이란 프로그램 그룹의 모음인데 세션 생성시 ttv를 부여하지 않게 되면 터미널을 갖지 않는 세션이 생겨나며 세션의 리더가 된다.


(3) 프로세스의 위치를 루트로 옮겨주면 경로 작업 수행에 유리하다(선택)


 * 또한 윈도우에서는 데몬을 서비스라고 부른다.





RAII는 C++ 진영에서 자주 쓰이는 Idiom으로 자원의 안전한 사용을 위해서 객체가 쓰이는 Scope를 벗어나면 자원을 해제해주는 기법이다. 메모리 누수를 관리하는 효과적인 기법이며, 제대로만 사용하면 오히려 별도의 관리 모듈이 붙는 것보다 안정적이다.


이는 C++ 에서 자원을 얻을 때 초기화가 되어야 하며, 유효한 객체 형태가 반환되어야하는 것을 의미한다. 반대로 객체가 사라질 때에는 가진 자원을 전부 반환해야 하며 객체가 유효하지 않은 형태가 되어야 한다.


 그리고 이것은 C++ 의 메모리 누수를 관리하는 철학이며, 이를 Scope 단위로 관리하고자 한다.


 그렇기 때문에 C/C++ 과 같은 통칭 .NET 등에서 Unmanaged 라 불리는 언어들을 다룰 때에는 메모리를 사용하는 각 변수들의 유효한 Scope 를 정확히 파악하는 것이 아주 중요하다.


 heap의 자원은 명시적 해제 이전에 해제되지 않지만 Stack 변수의 경우 Scope를 벗어나면 Destructor가 호출되는 것을 이용한 것이다.


 Exception 등 Control Flow Breaking 시에도 RAII를 이용하여 자원관리를 할 수 있으며 C++의 제작단계에서 스트로스트룹이 finally를 굳이 고안하지 않은 이유이기도 하다.



물론 이러한 특성들이 많은 개발자들의 C++의 진입 장벽을 높이는데에 포인터와 같이 큰 몫을 차지하고 있기 때문에 C++11 등 모던 C++에서는 여러 가지 장치들을 통해 메모리를 관리해주려는 노력을 하고 있다.


'Programming Language > C&C++' 카테고리의 다른 글

Stdafx.h 란?  (0) 2018.09.25
[Old] C++Type Casting 정리  (0) 2018.09.25

+ Recent posts