1. 문제

 

Given a binary tree, return the sum of values of nodes with even-valued grandparent.  (A grandparent of a node is the parent of its parent, if it exists.)

If there are no nodes with an even-valued grandparent, return 0.

 

 

 

2. 예시

 

 

Input: root = [6,7,8,2,7,1,3,9,null,1,4,null,null,null,5]
Output: 18
Explanation: The red nodes are the nodes with even-value grandparent while the blue nodes are the even-value grandparents.

 

 

 

3. 풀이

 

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var sumEvenGrandparent = function(root) {
    let result = 0;
    let trav = function(node, pa, gpa) {
        if(!node) return;
        if(gpa && gpa%2 === 0){
            result += node.val;
        }
        gpa = pa;
        pa = node.val;
        trav(node.left, pa, gpa);
        trav(node.right, pa, gpa);
    };
    trav(root, null, null);
    return result;
};

 

 

 

4. 결과

 

14 / 14 test cases passed.
Status: Accepted
Runtime: 84 ms
Memory Usage: 42.8 MB

 

 

 

반응형

 

1. 문제

 

Given a valid (IPv4) IP address, return a defanged version of that IP address.

A defanged IP address replaces every period "." with "[.]".

 

 

 

2. 예시

 

Example 1:
Input: address = "1.1.1.1"
Output: "1[.]1[.]1[.]1"

 

Example 2:
Input: address = "255.100.50.0"
Output: "255[.]100[.]50[.]0"

 

 

 

3. 풀이

 

/**
 * @param {string} address
 * @return {string}
 */
var defangIPaddr = function(address) {
    return address.split('.').join('[.]');
};

 

 

 

4. 결과

 

62 / 62 test cases passed.
Status: Accepted
Runtime: 52 ms
Memory Usage: 33.7 MB

 

 

 

5. 다른 접근 방법

 

정규식과 repace 함수를 사용해 .을 [.]로 치환한다. 

 

 

 

반응형

웹 서핑을 하다 흥미로운 주제를 발견하여 번역 글을 올립니다.

해당 글은 Rhea MoutafisWhy Python is not the programming language of the future를 번역한 글입니다.

 

Python - Photo by David Clode on Unsplash

 

0. 앞선 글

 

최근 몇 년간 파이썬은 아주 핫한 언어가 아닐 수 없습니다. 직접 사용도 해보고 주변 사람들에게서 전해 들은 내용도 있으며 심지어 요즘 대학 강의에서도 가장 처음 배우는 언어 중 하나로 꼽히기도 한답니다. 

 

실제로 스택 오버 플로우의 2015~2019년간 조사한 프로그래밍 언어의 인기도를 봐 보겠습니다.

 

 

차트에서 보듯이 C/C++, JAVA와 박빙의 승부를 벌이고 있습니다. 이러한 상황에서 왜 파이썬은 미래의 언어가 아니라고 한 걸까요? 원글의 저자는 글 제목에 "몇 년은 더 수요가 많을지라도"를 덧붙였습니다. 이 대세 언어가 왜 미래의 언어가 될 순 없는지 약간은 자극적인 제목의 글에 끌려 읽은 글의 내용을 공유해보고자 합니다.

 

 

 

1. 소개

 

프로그래밍 커뮤니티가 파이썬을 평가하는 데에는 수십 년이 걸렸습니다. 그러나 2010년 초부터 인기가 급격히 상승하며 C, C#, JAVA, JavaScript의 인기를 뛰어넘었습니다.
그런데 이러한 트렌드가 언제까지 지속될까요? 파이썬은 언제 그리고 왜 다른 언어로 교체될까요?
파이썬 세계의 정확한 만료일을 정하는 것은 공상 과학만큼 너무나도 추측일 뿐입니다. 그 대신에 현재 파이썬의 인기를 높이는 장점과 미래에 인기를 깨뜨릴 약점에 대해서 평가할 것입니다. 

 

 

 

 

2. 현재 파이썬이 인기 있는 이유

 

파이썬의 성공은 플랫폼의 포스트에 있는 태그의 수를 측정하는 스택 오버플로우 트렌드에 반영됩니다. 스택 오버플로우의 크기로 볼 때 이는 언어 인기에 꽤나 좋은 지표입니다.

 

 

Snapshot of tags on various programming languages

 

R은 수년간 정체되어 왔으며 다른 많은 언어들은 꾸준히 감소세에 있는 반면에 파이썬의 성장은 막을 수 없어 보입니다. 모든 스택 오버플로우 질문 중 거의 14%에 파이썬 태그가 붙어있으며 이러한 트렌드는 증가하는 추세입니다. 여기에는 몇 가지 이유가 있습니다.

 

 

2.1. 파이썬은 오래되었습니다.

 

파이썬은 1990년대부터 존재해 왔습니다. 그것은 성장에 충분한 시간이었을 뿐 아니라 대규모의 지원 커뮤니티까지 확보하였습니다. 

 

따라서 파이썬으로 코딩하는 동안 문제가 발생하면 구글 검색 한 번으로 문제를 해결할 가능성이 높습니다. 누가 이미 문제를 겪고 문제에 대해 도움이 되는 무언가를 적어 놨을 것이기 때문입니다.

 

 

2.2. 파이썬은 초보자가 배우기 좋습니다.

 

수십 년은 프로그래머들이 뛰어난 튜토리얼을 만들 시간이었습니다. 그 외에도 파이썬의 문법은 사람이 읽기 쉽습니다.

 

시작을 위해 데이터 유형을 지정할 필요가 없습니다. 단시 변수를 선언하기만 하면 됩니다. 파이썬은 콘텍스트에서 그것이 integer인지, float인지, boolean인지 혹은 또 다른 것인지 알아서 이해합니다. 이것은 초보자에게 매우 큰 장점입니다.  C++로 프로그래밍한 적이 있다면 float를 int로 바꿨을 때 프로그램이 컴파일되지 않는 게 얼마나 실망스러운지 알 것입니다. 

 

그리고 파이썬과 C++ 코드를 비교해가며 읽어본 적이 있다면 파이썬이 얼마나 이해하기 쉬운지 알 수 있을 것입니다. C++는 영어를 염두하고 설계되었지만, 파이썬 코드와 비교하면 읽기에 더 어렵습니다. 

 

 

2.3. 파이썬은 다목적으로 사용할 수 있습니다.

 

파이썬은 오랫동안 사용되어 왔기 때문에 개발자들은 모든 목적에 맞는 패키지를 만들었습니다. 요즘에는 거의 모든 것들을 위한 패키지를 찾을 수 있습니다. 

 

숫자, 벡터, 행렬을 다루고 싶습니까? NumPy가 있습니다.

테크 혹은 엔지니어링에 대한 계산을 원합니까? SciPy를 사용하시면 됩니다.

데이터를 다루거나 분석하는데 관심이 있습니까? Pandas로 가십시오.

인공지능을 시작하고 싶습니까? 왜 Scikit-Learn을 사용하지 않나요?


관리하려는 계산 작업이 무엇이든 그것을 위한 파이썬 패키지가 있을 수 있습니다. 이것은 지난 몇 년간 머신러닝의 수요가 급증할 때 파이썬 최근 개발 언어에서 최고에 머무르게 하였습니다.

 

 

 

3. 파이썬의 단점과 치명적인지에 대한 여부.

 

이전의 정교함으로 완성된 작업으로 인해 파이썬이 앞으로 몇 년간은 계속될 것으로 상상할 수 있습니다. 그러나 모든 기술이 그렇듯이 파이썬에는 약점이 있습니다. 가장 중요한 결점들을 하나씩 살펴본 후 이것이 치명적인가를 평가하겠습니다.

 

 

3.1. 속도

 

파이썬은 느립니다. 진짜 느려요. 파이썬으로 작업을 완료하는데 드는 시간은 다른 언어보다 평균적으로 2~10배나 더 필요합니다.

 

느린 이유는 여러 가지가 있습니다. 그중 하나는 타입이 동적으로 정해진다는 것입니다. 앞서 적은 내용 중에 다른 언어처럼 데이터 유형을 지정할 필요가 없다는 것을 기억하시나요? 이건 프로그램이 어떤 경우에도 동작하는 변수에 대해 충분한 공간을 점유해야 하기 때문에 많은 메모리를 사용해야 함을 의미합니다. 그리고 많은 메모리 사용은 많은 컴퓨팅 시간으로 이어집니다.

 

또 다른 이유는 파이썬이 한 번에 하나의 작업만 수행할 수 있기 때문입니다. 이는 유연한 데이터 타입의 결과입니다. 파이썬은 각 변수에 하나의 데이터 타입만 있는 것을 보장해야 하며 병렬 프로세스는 이를 망칠 수 있습니다. 

 

이에 비해 일반적인 웹 브라우저는 열두 개의 다른 스레드를 동시에 실행할 수 있습니다.  그리고 몇몇 다른 이론들 또한 있습니다. 그러나 날이 지나면 속도 문제는 중요하지 않습니다. 컴퓨터와 서버는 너무 저렴해져서 초단위 미만에 대해 이야기하고 있습니다. 그리고 엔드유저는 앱이 0.001초 또는 0.01초 만에 로드되는지 전혀 신경 쓰지 않습니다.

 

 

3.2. 범위(Scope)

 

원래 파이썬은 동적으로 범위가 지정되었습니다. 이건 기본적으로 모든 표현식을 평가하기 위해 컴파일러가 먼저 현재 플록을 검색한 뒤 연속적으로 모든 호출 함수를 검색한다는 걸 의미합니다.

 

동적 범위 지정의 문제점은 모든 표현식이 모든 가능한 상황에서 테스트되어야 한다는 점입니다. 그렇기 때문에 대부분의 모던 프로그래밍 언어는 정적 범위를 사용합니다. 

 

파이썬은 정적 범위로 전환을 시도하였으나 실패하였습니다. 일반적으로 내부 범위(예를 들면 함수 내의 함수)는 외부 범위를 보고 외부 범위를 변경할 수 있습니다. 파이썬에서 내부 범위는 외부 범위를 볼 순 있지만 외부 범위를 변경할 순 없습니다. 이것은 많은 혼란을 초래합니다.

 

 

3.3. 람다

 

파이썬의 모든 유연성에도 불구하고 람다의 사용은 제한적입니다. 람다는 파이썬에서 표현식(Expression)에서만 사용될 수 있고 구문(Statement)에선 사용할 수 없습니다.

 

반면에 변수 선언과 구문은 항상 구문입니다. 이는 람다를 변수 선언에 사용할 수 없음을 의미합니다. 표현식과 구문의 구별은 약간 멋대로이며 다른 언어에서는 발생하지 않습니다. 

 

 

3.4. 공백

 

파이썬에서 다른 레벨의 코드를 공백과 들여 쓰기를 사용하여 나타냅니다. 이는 시각적으로 매력적이고 직관적으로 이해할 수 있도록 만듭니다.

 

C++와 같은 다른 언어에서는 중괄호와 세미콜론에 의존합니다. 이는 시작적으로 매력적이지 않고 초보자에게 친숙하지 않지만 코드를 유지 관리하기 훨씬 쉽습니다. 대규모 프로젝트에서 이는 매우 유용합니다. Haskell과 같은 최신 언어는 이러한 문제를 다음과 같이 해결합니다: 공백에 의존하지만 공백을 대체할 문법을 제공합니다.

 

 

3.5. 모바일 개발

 

데스트톱에서 스마트폰으로의 움직임이 보임에 따라 우리는 모바일 소프트웨어를 구축하기 위해 견고한 언어가 필요합니다.

 

하지만 많은 모바일 앱들이 파이썬으로 개발되지 않습니다. 그렇다고 그것이 알 수 없다는 의미는 아닙니다. 모바일 앱 개발을 위해 파이썬은 Kivy라는 패키지를 제공합니다.

 

그러나 파이썬은 모바일을 염두하고 만들어지지 않았습니다. 그래서 기본적인 작업애 대해서는 적합한 결과를 얻을 수도 있지만 모바일 앱 개발을 위해 만들어진 언어를 사용하는 것이 가장 좋습니다. 모바일을 위해 널리 사용하는 프로그래밍 프레임워크에는 React Native, Flutter, Iconic, Cordova가 있습니다.

 

명백히 랩톱과 데스크톱 컴퓨터는 다가울 수년간 존재할 것입니다. 그러나 모바일은 오랫동안 데스크톱 트래픽을 뛰어넘었기 때문에 파이썬을 배우는 것 만으로는 숙달된 만능 개발자가 되기에는 부족합니다. 

 

 

3.6. 런타임 에러

 

파이썬 스크립트는 먼저 컴파일된 뒤에 실행되는 것이 아닙니다. 그 대신 실행할 때마다 컴파일됩니다. 그러므로 런타임에 코딩 에러가 나타납니다. 이러한 점은 낮은 성능, 시간 소비, 많은 테스트를 요구하게 됩니다. 

 

테스팅은 초보자에게 많은 것을 가르쳐 주기 때문에 초보자들에게는 좋습니다. 그러나 숙련된 개발자에게는 파이썬으로 된 복잡한 프로그램을 디버그 해야 하기 때문에 잘못된 결과를 만들게 합니다.

 

 

 

4. 언제 그리고 무엇이 미래에 파이썬을 대체할 수 있을까.

 

프로그래밍 언어 시장에는 몇 개의 새로운 경쟁자가 나타났습니다:

  • Rust는 파이썬이 가진 것과 동일한 종류의 안전성을 제공합니다 - 실수로 덮어쓸 수 있는 변수는 존재하지 않습니다. 하지만 Rust는 소유권과 빌리는 개념으로 성능 문제를 해결합니다. 스택 오버플로우 인사이트에 따르면 지난 몇 년간 가장 사랑받는 프로그래밍 언어이기도 합니다.
  • Go는 파이썬과 같이 초보자에게 좋습니다. 그리고 심지어 코드를 유지보수 하기가 매우 간단합니다. 재미있는 점은 Go 개발자는 시장에서 가장 많은 보수를 받는 프로그래머 중 하나입니다. 
  • Julia는 파이썬과 정면에서 경쟁하는 매우 새로운 언어입니다. Julia는 매우 큰 기술적 계산의 간극을 채웁니다: 일반적으로 파이썬 또는 Matlab을 사용하고 모든 것을 C++ 라이브러리로 패치했습니다. 이제 Julia 하나로 다른 두 언어로 저글링을 할 필요가 없어졌습니다.

시장에 다른 언어들이 존재하지만 Rust, Go, Jilia는 파이썬을 조금만 수정하는 언어들입니다. 이러한 언어들은 모두 아직 오지 않은 기술들에 있어서 뛰어납니다. 특히 인공지능 분야에서 가장 주목할만합니다.  스택 오버플로우 태그의 수에 반영된 것처럼 아직 시장 점유율은 낮지만 이러한 언어들 모두의 트렌드는 상승할 것이 명확합니다.

 

Snapshot of tags on various programming languages

 

어디에서나 인기 있는 파이썬의 인기를 볼 때, 새로운 언어 중 하나가 파이썬을 대체할 때 까지는 분명히 5년이 걸릴 것이며 심지어는 10년이 걸릴 수도 있습니다.

Rust, Go, Julia 또는 미래의 새로운 언어 중 어떤 것이 파이썬을 대체할 것인지는 이 시점에서 말하기 어렵습니다. 그러나 파이썬 아키텍처에서 근본적인 성능 문제를 생각해 본다면 그중 하나는 반드시 파이썬을 대체할 것입니다. 

 

 

 

5. 마무리

 

글을 읽으면서, 읽고 나서 여러 의문점이 떠올랐습니다. 아니나 다를까 이 글에는 분명히 논쟁의 여지가 있습니다. 원문의 댓글창에서도 많은 의견들이 올라오고 있습니다. 

 

원 글은 올라온 지 2일 만에 8600여 개의 박수를 받았고 댓글도 120여 개에 달합니다. 그만큼 파이썬에 대한 인기와 그 문제점에 대해 명확히 지적하고 있음을 느낄 수 있습니다. 

 

이 글이 100% 완벽하고 비판의 여지가 없다고 생각하지는 않습니다. 읽고 계시는 분들도 자신이 파이썬을 바라보는 시각과 느끼고 계신 단점이 있을 테고 파이썬을 사용하는 장점 또한 당연히 갖고 계실 겁니다. 다만 이러한 글을 읽고 새로운 시각으로 파이썬을 보게 되거나 새로운 영감이 떠오르셨으면 좋겠습니다.

 

 

 

 

반응형

 

 

본격적인 postgresql 사용에 앞서 Database와 유저를 생성하고 pgAdmin4를 통해 외부 네트워크에서 접속을 시도해 봅니다.

 

 

 

1.  DATABASE 생성

 

이제 외부 접속을 허용했으니 사용할 DB를 생성합니다. 다음 명령어를 통해 Postgresql Shell을 실행합니다.

 

$ sudo su postgres

$ psql

 

 

이제 사용할 DB를 생성합니다. 

 

CREATE DATABASE smoh;

 

 

 

 

2. USER 생성

 

그다음 유저를 생성하고 권한을 부여합니다. 개발용 계정으로 사용할 예정이기 때문에 SUPAERUSER 권한을 줬습니다.

 

CREATE USER {user_name} WITH PASSWORD '{user_password}';

 

ALTER USER {user_name} WITH SUPERUSER;

 

 

만약 해당 유저에게 특정 권한만 주고 싶다면 다음 명령어를 사용하시면 됩니다.

 

GRANT {permissions} ON DATABASE {db_name} TO {user_name};

 

권한 리스트는 여기에서 자세히 알아볼 수 있습니다.

 

 

** 기본 계정 설정 변경.

기본 계정인 postgres의 기본 암호는 없습니다. 만약 새로 계정을 생성하지 않고 이 기본 계정을 사용하려면 암호를 변경해야만 합니다. 

 

기본 계정의 암호는 다음과 같은 SQL로 변경할 수 있습니다.

 

ALTER USER postgres WITH PASSWORD '{new_pass}';

 

 

 

3. pgAdmin 설치 및 사용

 

Postgresql을 위해 pgAdmin을 사용합니다. 여기서 OS 및 버전을 골라 다운로드하여 설치합니다.

 

실행하면 다음과 같은 화면이 나올 텐데 먼저 기본 암호를 설정해 줍니다.

 

 

암호 설정 후 서버를 추가합니다.

 

 

이후 서버 접속정보에 위에서 생성한 계정 정보 및 DB 정보를 입력하면 정상 사용 가능합니다.

 

 

 

 

반응형

 

 

외부 네트워크에서 postgresql에 접속할 수 있도록 하는 방법에 대해 알아봅니다.

 

 

 

1. postgresql.conf

 

Postgresql은 기본적으로 외부에서의 접속을 막습니다. 따라서 외부에서 접속할 수 있도록 설정 변경이 필요합니다.

해당 설정은 postgresql.conf 파일에 있습니다. 해당 파일의 위치는 다음과 같습니다.

 

$ cat /etc/postgresql/12/main/postgresql.conf

 

 

이제 여기서 "listen_address"값을 수정해야 합니다. "CONNECTIONS AND AUTHENTICATION"섹션에 있습니다.

이 값을 vi로 수정해 줍시다. 만약 기본 포트를 사용하고 싶지 않다면 여기서 수정해 줍니다.

 

 

코멘트에 쓰여있는 대로 postgresql을 재시작해줍시다.

 

$ sudo service postgresql restart

 

 

 

2. pg_hba.conf

 

이 외에도 pg_hba.conf 파일 또한 수정해 줘야 합니다. 이 파일은 다음 위치에 존재합니다.

 

 

여기서 "IPv4 local connection"을 찾아 127.0.0.1/32가 아닌 모든 ip에서 접속할 수 있도록 수정합니다.

 

다시 한번 postgresql 서비스를 재시작합니다.

 

$ sudo service postgresql restart

 

 

 

 

반응형

 

Ubuntu OS에 Postgresql을 설치하는 방법에 대해서 알아봅니다.

 

 

 

1. Ubuntu OS 준비.

 

Ubuntu OS를 미리 준비합니다. 이 글에서는 ubuntu server 18.04.3 버전을 사용합니다. 

Postgresql 공식 홈페이지의 설명에 따르면 Postgresql은 Ubuntu 16.04, 18.04 이상의 버전을 요구합니다.

우분투 설치 방법에 대해서는 별도로 다루지 않습니다.

 

 

 

2. Postgresql 설치 버전 확인.

 

우선 다음 명령어를 통해 현재 사용 가능한 postgresql 버전을 확인합니다.

 

$ apt show postgresql

 

 

버전 정보가 보이시나요? 현재 posrgresql로 설치하면 postgresql 10 버전이 설치됩니다.

만약 별다른 추가 작업 없이 그냥 pgsql10 버전을 설치하고 싶다면 다음 명령어로 바로 설치할 수 있습니다.

 

$ sudo apt install postgresql postgresql-contrib

 

하지만 아래의 Postgresql 공식 홈패이지 우분투 배포 정보 페이지를 보면 글 작성 시점에 10 버전보다 최신 버전이 이미 배포되어 있습니다.

 

 

이제 postgresql-11의 apt정보를 봐 볼까요?

 

$ sudo apt show postgresql-11

 

 

안타깝게도 postgresql 10 이후의 버전은 별도의 리포지토리 등록 작업이 필요합니다.

 

 

 

3. Postgresql 설치를 위한 리포지토리 등록.

 

우선 pgdg.list 파일을 만든 후 다음 내용을 추가해 줍니다.

 

deb http://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main

 

 

 

그런 후 다음 명령어를 통해 GPG키를 추가합니다.

 

$ wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - 

 

마지막으로 업테이트를 수행해 줍니다.

 

$ sudo apt-get update

 

 

이제 진짜 postgresql의 최신 버전을 사용할 수 있는지 확인해 봅시다.

 

$ sudo apt show postgresql

 

 

 

4. Postgresql 설치.

 

이제 현재 사용할 수 있는 최신버전의 postgresql-12를 설치해 보도록 합니다.

 

다음 명령어를 통해 postgresql을 설치합시다.

 

$ sudo apt-get install postgresql postgresql-contrib

 

 

** postgresql-contrib이란?

postgresql-contrib 또는 contrib 패키지는 핵심 PostgreSQL 패키지의 일부가 아닌 일부 추가 유틸리티와 기능으로 구성됩니다. 대부분의 경우 PostgreSQL 코어와 함께 contrib 패키지를 설치하는 것이 좋습니다.

 

이제 다음 명령어로 postgresql이 정상적으로 설치, 동작하는 것을 확인할 수 있습니다.

 

$ service postgresql status

 

 

 

 

 

반응형

GraphQL에 대해 알아보는 글을 포스팅합니다. 이 글에선 GraphQL의 구조에 대해 알아봅니다. 

이전 글인 Graphql에 대한 기본적인 소개와 REST API와의 비교를 통한 차이점은 다음 글에서 확인해 볼 수 있습니다.

 

 

 

 

 

 

 

1. 쿼리(Query)와 뮤테이션(Mutation)

 

쿼리는 읽기를 요청하는 구문이며 뮤테이션은 수정을 요청하는 구문입니다. gql에서는 굳이 쿼리와 뮤테이션을 나누는데 사실상 이 둘은 별 차이가 없어 보입니다. 쿼리는 데이터를 읽는데(R) 사용하고, 뮤테이션은 데이터를 변조(CUD) 하는 데 사용한다는 개념 적인 규약을 정해 놓은 것뿐입니다.

 

다만 쿼리가 실행되는 순서에 중요한 차이점이 있는데 Query 필드가 병렬로 실행되는 동안 Mutation 필드는 순차적으로 실행됩니다. 즉, 하나의 요청에서 두 개의 Mutation를 보내면 순차적으로 실행되어 경쟁 상태가 되지 않도록 합니다. (GraphQL의 구현 조건입니다.)

 

다음 두 쿼리문을 한번 봅시다.

{
  human(id: "1000") {
    name
    height
  }
}

query HeroNameAndFriends($episode: Episode) {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}

 

두 쿼리 문의 차이가 보이시나요? 결론부터 말하면 위의 쿼리는 오퍼레이션 이름(Operation name)이 붙지 않은 일반적이 인 오퍼레이션 즉, gql의 기본 쿼리문이고 아래의 쿼리는 오퍼레이션 이름이 붙은 쿼리입니다.

** 오퍼레이션(Operation)은 GraphQL 실행 엔진으로 해석할 수 있는 단일 쿼리(Queyr), 뮤테이션(Mutation) 또는 구독(Subscription)을 의미합니다.

 

일반적인 경우 클라이언트에서 static 한 쿼리문을 작성하지는 않을 것입니다. 주로 정보를 불러올 때 id 값이나, 다른 인자 값을 가지고 데이터를 불러올 것입니다. gql을 구현한 클라이언트에서는 이 변수에 프로그래밍으로 값을 할당할 수 있는 함수 인터페이스가 존재합니다. 

 

오퍼레이션 이름이 있는 쿼리

 

위의 그림을 통해 오퍼레이션에 대해 좀 더 자세히 알아보겠습니다. 

  • 오퍼레이션 타입: 어떤 유형의 작업을 수행하고자 하는지 표시합니다. query, mutation 또는 subscription입니다. 
  • 오퍼레이션 이름: 오퍼레이션에 이름을 부여합니다. 프로그래밍 언어의 함수 이름과 유사합니다. 이름을 정해두면 네트워크 로그나 GraphQL 서버에서 문제가 발생했을 시 내용을 해독할 필요 없이 코드 베이스에서 해당 쿼리를 쉽게 찾을 수 있습니다.
  • 변수 정의: 쿼리에 필요한 변수입니다. GraphQL 서버로 쿼리를 보낼 때 실제 쿼리는 동일하지만 요청마다 변경되는 동적 부분이 있을 수 있습니다. 이때 변수가 사용됩니다. 이 변수는 쿼리와 별도로 전송되며 전송 포맷은 JSON입니다.

 

오퍼레이션 이름이 붙은 쿼리는 데이터베이스에서의 프로시저(Procedure) 개념과 유사하다고 생각하면 됩니다.

이 개념 덕분에 REST API를 호출할 때와 다르게, 한 번의 인터넷 네트워크 왕복으로 여러분이 원하는 모든 데이터를 가져올 수 있습니다.

 

데이터베이스의 프로시저는 DBA 혹은 백앤드 프로그래머가 작성하고 관리하였지만, gql 오퍼레이션은 는 클라이언트 프로그래머가 작성하고 관리합니다.

이전 협업 방식(REST API)에서는 프런트 앤드 프로그래머는 백앤드 프로그래머가 작성하여 전달하는 API의 request / response의 형식에 의존하게 됩니다. 그러나, gql을 사용한 방식에 서는 이러한 의존도가 많이 사라집니다만 여전히 데이터 스키마에 대한 협업 의존성은 존재합니다.

 

 

2. 스키마(Schema)와 타입(Type)

 

데이터베이스 스키마(Schema)를 작성할 때의 경험을 SQL 쿼리 작성으로 비유한다면, gql 스키마를 작성할 때의 경험은 C, C++의 헤더 파일 작성에 비유가 됩니다. 그러므로, 프로그래밍 언어(C, C++, JAVA 등)에 익숙한 프로그래머라면 스키마 정의 또한 쉽게 배울 수 있습니다.

 

GraphQL 스키마의 가장 기본적인 구성 요소는 객체(Object) 타입입니다. 객체 타입은 서비스에서 가져올 수 있는 객체의 종류와 그 객체의 필드를 나타냅니다. GraphQL 스키마 언어에서는 다음과 같이 표현할 수 있습니다.

 

type Character {
  name: String!
  appearsIn: [Episode]!
}

 

위 스키마에 대해 좀 더 자세히 알아봅시다.

  • Character: GraphQL의 객체 타입. 즉, 필드가 있는 타입이 됩니다. 대부분의 스키마는 객체 타입입니다.
  • name과 appersIn: Character 타입의 필드.  name과 appearsIn은 GraphQL 쿼리의 Character 타입 어디서든 사용할 수 있는 필드가 됩니다.
  • String: 내장된 스칼라 타입 중 하나. 스칼라 객체로 해석되는 타입을 의미하며 쿼리에서 하위 선택을 할 수 없습니다.
  • !: 해당 필드가 NULL을 허용하지 않음을 의미합니다. 이 필드를 쿼리 할 때 GraphQL 서비스가 항상 값을 반환함을 의미합니다. 
  • [Episode]!: Episode 객체의 배열을 나타내며 NULL이 아님을 의미합니다. 따라서 appearIn 필드를 쿼리 하면 항상 0개 이상의 아이템을 가진 배열을 기대할 수 있습니다.

 

 

3.3 리졸버(Resolver)

 

데이터베이스 사용 시, 데이터를 가져오기 위해서 sql을 작성했습니다. 또한, 데이터베이스에는 데이터베이스 애플리케이션을 사용하여 데이터를 가져오는 구체적인 과정이 구현되어 있습니다. 그러나 gql에서는 데이터를 가져오는 구체적인 과정을 직접 구현해야 합니다. 


gql 쿼리문 파싱은 대부분의 gql 라이브러리에서 처리를 하지만, gql에서 데이터를 가져오는 구체적인 과정은 resolver(이하 리졸버)가 담당하고, 이를 직접 구현해야 합니다.  프로그래머는 리졸버를 직접 구현해야 하는 부담은 있지만, 이를 통해서 데이터 source의 종류에 상관없이 구현이 가능합니다. 


예를 들어서, 리졸버를 통해 데이터를 데이터베이스에서 가져올 수 있고, 일반 파일에서 가져올 수 있고, 심지어 http, SOAP와 같은 네트워크 프로토콜을 활용해서 원격 데이터를 가져올 수 있습니다. 
이러한 특성을 이용하면 legacy 시스템을 gql 기반으로 바꾸는데 활용할 수 있습니다.

gql 쿼리에서는 각각의 필드마다 함수가 하나씩 존재한다고 생각하면 됩니다. 이 함수는 다음 타입을 반환합니다. 이러한 각각의 함수를 리졸버(resolver)라고 합니다. 다시 말하자면 리졸버는 쿼리에서 특정 필드에 대한 요청이 있을 때, 그것을 어떤 로직으로 처리할지 GraphQL에게 알려주는 역할을 맡습니다. 만약 필드가 스칼라 값(문자열이나 숫자와 같은 primitive 타입)인 경우에는 실행이 종료됩니다. 즉 더 이상의 연쇄적인 리졸버 호출이 일어나지 않습니다. 
하지만 필드의 타입이 스칼라 타입이 아닌 우리가 정의한 타입이라면 해당 타입의 리졸버를 호출되게 됩니다.

 

다음 예시를 통해 리졸버에 대해 좀 더 알아봅시다.

 

Query: {
  human(obj, args, context, info) {
    return context.db.loadHumanByID(args.id).then(
      userData => new Human(userData)
    )
  }
}

 

이 예시에서 Query 타입은 인자 id를 받아 human 필드를 반환합니다. 이 필드의 리졸버 함수는 데이터베이스에 접근한 다음 Human 객체를 생성하고 반환합니다. 이 예시는 자바스크립트로 작성되었지만 GraphQL 서버는 다양한 언어로 만들 수 있습니다. 

 

리졸버 함수는 네 개의 인수를 받습니다.

  • obj: 루트 Query 타입 이전의 객체.
  • args: GraphQl 쿼리의 필드에 제공된 인수들.
  • context: 모든 리졸버 함수에 전달되며 현재 로그인한 사용자, 데이터베이스 액세스 등 중요한 컨텍스트 정보를 갖고 있는 값.
  • info: 현재 쿼리, 스키마 정보와 관련된 빌드 별 정보를 갖고 있는 값.

 

예시에서 볼 수 있듯이 각각의 리졸버 함수에는 내부적으로 데이터베이스 쿼리가 존재합니다. 이 말인즉, 쿼리에 맞게 필요한 만큼만 최적화하여 호출할 수 있다는 의미입니다.

기존에 REST API 시대에는 정해진 쿼리는 무조건 전부 호출이 되었습니다. 이러한 리졸버 체인을 잘 활용한다면, 효율적인 설계가 가능합니다. 

 

 

 

4. 인스트로펙션(Instropection, 스키마 확인)

 

기존 서버-클라이언트 협업 방식에서는 연동 규격서라고 하는 API 명세서를 주고받는 절차가 반드시 필요했습니다. 프로젝트 관리 측면에서 관리해야 할 대상의 증가는 작업의 복잡성 및 효율성 저해를 의미합니다. 이 API 명세서는 때때로 관리가 제대로 되지 않아, 인터페이스 변경 사항을 제때 문서에 반영하지 못하기도 하고, 제 타이밍에 전달 못하곤 합니다.

이러한 REST의 API 명세서 공유와 같은 문제를 해결하는 것이 gql의 인트로스펙션 기능입니다. gql의 인트로스펙션은 서버 자체에서 현재 서버에 정의된 스키마의 실시간 정보를 공유를 할 수 있게 합니다. 이 스키마 정보만 알고 있으면 클라이언트 사이드에서는 따로 연동 규격서를 요청할 필요가 없게 됩니다. 클라이언트 사이드에서는 실시간으로 현재 서버에서 정의하고 있는 스키마를 의심할 필요 없이 받아들이고, 그에 맞게 쿼리문을 작성하면 됩니다.

이러한 인트로스펙션용 쿼리가 따로 존재합니다. 일반 gql 쿼리문을 작성하듯이 작성하면 됩니다. 다만 실제로는 굳이 스키마 인트로스펙션을 위해 gql 쿼리문을 작성할 필요가 없습니다. 대부분의 서버용 gql 라이브러리에는 쿼리용 IDE를 제공합니다. 

 

프로그래머는 인트로스펙션을 활용하여, 직접 쿼리 및 뮤테이션, 필드 스키마를 확인할 수 있습니다. 물론 보안상의 이슈로 상용 환경에서는 이러한 스키마의 공개는 신중해야 합니다. 대부분의 라이브러리는 해당 기능을 켜고 끄게 하는 옵션이 존재합니다.

 

아래의 예시로 인스트로펙션 쿼리와 그에 대한 결과를 확인해 봅시다.

일반적으로 타입 시스템을 사용하기 때문에 우리는 유효한 타입이 무엇인지 알고 있지만, 그렇지 않은 경우에는 Query의 루트 타입에서 항상 사용할 수 있는 __schema 필드를 쿼리 하여 GraphQL에 요청할 수 있습니다. 아래의 쿼리는 그 예시입니다.

 

{
  __schema {
    types {
      name
    }
  }
}

 

더보기

{
  "data": {
    "__schema": {
      "types": [
        {
          "name": "Query"
        },
        {
          "name": "Episode"
        },
        {
          "name": "Character"
        },
        {
          "name": "ID"
        },
        {
          "name": "String"
        },
        {
          "name": "Int"
        },
        {
          "name": "FriendsConnection"
        },
        {
          "name": "FriendsEdge"
        },
        {
          "name": "PageInfo"
        },
        {
          "name": "Boolean"
        },
        {
          "name": "Review"
        },
        {
          "name": "SearchResult"
        },
        {
          "name": "Human"
        },
        {
          "name": "LengthUnit"
        },
        {
          "name": "Float"
        },
        {
          "name": "Starship"
        },
        {
          "name": "Droid"
        },
        {
          "name": "Mutation"
        },
        {
          "name": "ReviewInput"
        },
        {
          "name": "__Schema"
        },
        {
          "name": "__Type"
        },
        {
          "name": "__TypeKind"
        },
        {
          "name": "__Field"
        },
        {
          "name": "__InputValue"
        },
        {
          "name": "__EnumValue"
        },
        {
          "name": "__Directive"
        },
        {
          "name": "__DirectiveLocation"
        }
      ]
    }
  }
}

* 접은 글을 펼치면 예시 결과를 확인할 수 있습니다.

 

  • Query, Character, Human, Episode, Droid: 타입 시스템에서 정의한 것들입니다.
  • __Schema, __Type, __TypeKind, __Field, __InputValue, __EnumValue, __Directive: 모두 앞에는 두 개의 밑줄이 붙어있는데, 이것은 인스트로펙션 시스템의 일부임을 나타냅니다.

 

어떤 객체가 어떤 필드를 사용할 수 있는지 알아보기 위해 예시로 Droid를 인스트로펙션 시스템에 요청해 봅시다.

 

{
  __type(name: "Droid") {
    name
    fields {
      name
      type {
        name
        kind
      }
    }
  }
}
더보기

{
  "data": {
    "__type": {
      "name": "Droid",
      "fields": [
        {
          "name": "id",
          "type": {
            "name": null,
            "kind": "NON_NULL"
          }
        },
        {
          "name": "name",
          "type": {
            "name": null,
            "kind": "NON_NULL"
          }
        },
        {
          "name": "friends",
          "type": {
            "name": null,
            "kind": "LIST"
          }
        },
        {
          "name": "friendsConnection",
          "type": {
            "name": null,
            "kind": "NON_NULL"
          }
        },
        {
          "name": "appearsIn",
          "type": {
            "name": null,
            "kind": "NON_NULL"
          }
        },
        {
          "name": "primaryFunction",
          "type": {
            "name": "String",
            "kind": "SCALAR"
          }
        }
      ]
    }
  }
}

* 접은 글을 펼치면 예시 결과를 확인할 수 있습니다.

 

결과를 확인해 보면 "NON_NULL" 타입의 경우 "name"에 null인 것을 확인할 수 있습니다. 종류가 "NON_NULL"인 wrapper 타입으로 인식하기 때문입니다.

비슷하게, friends와 appearIn 둘 다 LIST wrapper 타입이기 때문에 이름이 없습니다. 이 타입에 대해 ofType을 쿼리 할 수 있습니다. 그러면 이 list 가 어떤 list 인지 알 수 있습니다.

 

{
  __type(name: "Droid") {
    name
    fields {
      name
      type {
        name
        kind
        ofType {
          name
          kind
        }
      }
    }
  }
}
더보기

{
  "data": {
    "__type": {
      "name": "Droid",
      "fields": [
        {
          "name": "id",
          "type": {
            "name": null,
            "kind": "NON_NULL",
            "ofType": {
              "name": "ID",
              "kind": "SCALAR"
            }
          }
        },
        {
          "name": "name",
          "type": {
            "name": null,
            "kind": "NON_NULL",
            "ofType": {
              "name": "String",
              "kind": "SCALAR"
            }
          }
        },
        {
          "name": "friends",
          "type": {
            "name": null,
            "kind": "LIST",
            "ofType": {
              "name": "Character",
              "kind": "INTERFACE"
            }
          }
        },
        {
          "name": "friendsConnection",
          "type": {
            "name": null,
            "kind": "NON_NULL",
            "ofType": {
              "name": "FriendsConnection",
              "kind": "OBJECT"
            }
          }
        },
        {
          "name": "appearsIn",
          "type": {
            "name": null,
            "kind": "NON_NULL",
            "ofType": {
              "name": null,
              "kind": "LIST"
            }
          }
        },
        {
          "name": "primaryFunction",
          "type": {
            "name": "String",
            "kind": "SCALAR",
            "ofType": null
          }
        }
      ]
    }
  }
}

* 접은 글을 펼치면 예시 결과를 확인할 수 있습니다.

 

 

 

 

 

References

반응형

GraphQL에 대해 알아보는 글을 포스팅합니다. 이 글에선 Graphql에 대한 기본적인 소개와 REST API와의 비교를 통한 차이점을 알아보도록 합니다.

 

 

 

1. 소개

 

 

GraphQL은 페이스북에서 만든 쿼리 언어입니다. GrpahQL은 JavaScript 개발 환경에서 Data Layer와 관련하여 항상 언급되는 주제입니다만, 현재를 기준으로 아직 성숙한 쿼리 언어라곤 할 수 없습니다. 하지만 등장한 지 얼마 되지 않았음에도 불구하고, GraphQL의 인지도와 인기는 매우 빠르게 상승하고 있음을 확인할 수 있습니다.

 

JS - DataLayer 인지도, 관심도, 만족도 순위

 

GraphQL 설문 응답 자료

 

Graph QL(이하 gql)은 Structed Query Language(이하 sql)와 마찬가지로 쿼리 언어입니다. 하지만 gql과 sql의 언어적 구조 차이는 매우 큽니다. 또한 gql과 sql이 실전에서 쓰이는 방식의 차이도 매우 큽니다. 

gql과 sql의 언어적 구조 차이가 활용 측면에서의 차이를 가져왔습니다. 이 둘은 애초에 탄생 시기도 다르고 배경도 다릅니다. 

sql은 데이터베이스 시스템에 저장된 데이터를 효율적으로 가져오는 것이 목적이고, gql은 웹 클라이언트가 데이터를 서버로부터 효율적으로 가져오는 것이 목적입니다. sql의 문장(statement)은 주로 백앤드 시스템에서 작성하고 호출하는 반면, gql의 문장은 주로 클라이언트 시스템에서 작성하고 호출합니다.

 

sql 쿼리 예시

SELECT USER_EMAIL, USER_NAME FROM USERS WHERE USER_KEY = 1;

 

gql 쿼리 예시

query{
    users(user_key: "1"){
        user_email
        user_name
    }
}

 

서버사이드 gql 애플리케이션은 gql로 작성된 쿼리를 입력으로 받아 쿼리를 처리한 결과를 다시 클라이언트로 돌려줍니다. 

HTTP API 자체가 특정 데이터베이스나 플랫폼에 종속적이지 않은것 처럼 마찬가지로 gql 역시 어떠한 특정 데이터베이스나 플렛폼에 종속적이지 않습니다. 심지어 네트워크 방식에도 종속적이지 않습니다. 

일반적으로 gql의 인터페이스 간 송수신은 네트워크 레이어 L7의 HTTP POST 메서드와 웹소켓 프로토콜을 활용합니다. 필요에 따라서는 얼마든지 L4의 TCP/UDP를 활용하거나 심지어 L2 형식의 이더넷 프레임을 활용할 수도 있습니다.

 

gql 파이프 라인

 

 

 

2. GraphQL과 REST API

 

기존의 웹 혹은 모바일 애플리케이션의 API 를 구현할 때는, 통상적으로 REST API 가 사용됩니다.

기존의 REST API 를 사용하여 API를 구현을 한다면 우리가 클라이언트 사이드에서 어떠한 기능이 필요할 때마다 그때그때 새로운 API를 만들어야 했습니다.

문제는 나중에 어플리케이션의 규모가 커지면 수많은 Endpoint가 생성되게 됩니다. REST API는 URL, METHOD 등을 조합하기 때문에 다양한 Endpoint가 존재하게 되는 것입니다. 

 

일반적인 HTTP API 적용 스택

 

반면, gql은 단 하나의 Endpoint가 존재합니다. 또한, gql API에서는 불러오는 데이터의 종류를 쿼리 조합을 통해서 결정합니다.

예를 들면, REST API에서는 각 Endpoint마다 데이터베이스 SQL 쿼리가 달라지는 반면, gql API는 gql 스키마의 타입마다 데이터베이스 SQL 쿼리가 달라집니다.

 

GraphQL 적용 스택

 

아래의 그림과 같이 gql API를 사용하면 여러 번 네트워크 호출을 할 필요 없이, 한 번의 네트워크 호출로 처리할 수 있습니다.

 

REST API와 GraphQL API 사용의 차이점

 

 

 

 

 

 

* References

 

 

 

반응형

ORACLE에서 DECODE 함수와 CASE ... THEN 구문의 사용법 및 미묘한 차이점에 대해 알아봅니다.

 

 

 

1. 사용 목적

 

두 기능 모두 사용 목적은 조건문입니다. 두 기능 모두 일반적인 프로그래밍에서의 if/else 기능을 하고 있습니다.

 

 

 

2. DECODE 함수

 

DECODE는 다음과 같이 사용합니다.

 

DECODE( {대상}, {비교 값 1}, {결괏값 1}, {비교 값 2}, {결괏값 2}, ... , {비교 값 N}, {결괏값 N}, {나머지 값} )

 

이렇게만 두고 보면 알기 어려우니 예시를 통해 알아봅시다.

 

다음과 같은 테이블이 있을 때 DECODE 사용 예시와 그에 따른 결괏값 입니다.

 

 

예시 쿼리)

SELECT USER_KEY
	, USER_EMAIL
    , DECODE(USER_KEY, 1, '첫째 유저', 2, '둘째 유저', 3, '셋째 유저', '나머지 유저') USER_DESC 
FROM USERS;

 

결과)

 

 

3. CASE ... THEN 구문

 

CASE ... THEN 구문은 다음과 같이 사용합니다.

 

CASE
    WHEN {조건 1} THEN {결괏값 1}
    WHEN {조건 2} THEN {결괏값 2}
    ...
    WHEN {조건 N} THEN {결괏값 N}
    ELSE {나머지 값}
END

 

이렇게만 두고 보면 알기 어려우니 예시를 통해 알아봅시다.

 

다음과 같은 테이블이 있을 때 CASE 구문의 사용 예시와 그에 따른 결괏값입니다.

 

 

예시 쿼리)

SELECT USER_KEY     
	, USER_EMAIL
    , (CASE
    	WHEN USER_KEY = 1 THEN '첫째 유저'
        WHEN USER_KEY = 2 THEN '둘째 유저'
        WHEN USER_KEY = 3 THEN '셋째 유저'
        ELSE '나머지 유저'
       END) USER_DESC 
FROM USERS;    

 

결과)

 

 

4. 차이점

 

앞서 두 가지를 비교하면서 계속 언급했던 것을 보시면 가장 먼저 차이점이 와 닿을 것입니다. 

 

먼저 DECODE는 "함수"란 것이고 CASE는 "구문"이란것 입니다. 

따라서 DECODE는 SQL에서 사용할 수 없습니다. PL/SQL에서만 사용할 수 있는 함수입니다.

CASE구문은 STATEMENT이므로 SQL에서도 사용 가능합니다.

 

** SQL과 PL/SQL

SQL과 PL/SQL은 다릅니다. 

SQL은 RDBMS에서 데이터에 접근하기 위해 사용하는 "표준언어"입니다.

PL/SQL은 SQL을 사용하여 프로그램을 작성할 수 있게 "확장"해 놓은 오라클의 Procedual Language입니다.

 

 

 

 

 

 

 

반응형

SqlDeveloper에서 DATE 타입 컬럼 조회 시 시간까지 나오도록 하는 방법에 대해 알아봅니다.

 

 

 

1. 기본 조회 방식

 

SqlDeveloper에서 DATE 타입을 조회하면 기본값으로 년-월-일만 노출시켜줍니다.

등록된 시간까지 알고싶다면 직접 설정에 가서 조회 포맷을 변경시켜야 합니다.

 

 

 

2. 조회 포맷 수정

 

도구 > 환경설정으로 이동해서 수정해 봅시다.

 

환경설정에서 데이터베이스 > NLS > 날짜 형식을 다음과 같이 수정합니다.

 

YYYY/MM/DD HH24:MI:SS (해당 포맷은 원하는 대로 변경하셔도 됩니다.)

 

 

 

3. 수정된 포맷으로 조회하기

 

이제 다시 DATE 타입의 컬럼을 조회하면 다음과 같이 시간까지 보이는 것을 확인할 수 있습니다.

 

 

 

 

반응형

+ Recent posts