다른 서비스
NEW
기획
디자인
개발
프로덕트
아웃소싱
프리랜싱
비즈니스
최근 검색어
전체 삭제
최근 검색어가 없습니다.

개발

자바스크립트 개발자가 알고리즘을 C언어로 푸는 이유

 

자바스크립트는 유연하고 편리한 언어이다. 변수의 타입을 중간에 바꾸어도 되고, 문자열을 가지고 숫자처럼 연산할 수 있다. 자바스크립트는 내장 메서드를 풍부하게 제공하기에 간단한 기능 정도는 내장 메서드 호출로 구현할 수 있다. 사용법이 간단하고 최적화된 메서드 덕분에 자바스크립트로만 프로그래밍하면 알고리즘의 필요성을 느끼기 어렵다.

 

하지만 만약 구현하려는 기능이 더 커진다면 어떨까? 수행하는 절차가 많아진다면? 프로그래밍을 하다 보면 성능과 효율에 대해 고민해야 하는 시점은 반드시 오고, 바로 이럴 때 알고리즘 전략이 필요하다. 그리고 적절한 알고리즘을 잘 선택하는 능력은 훈련을 통해 키울 수 있다.

 

알고리즘 훈련을 할 때 프로그래밍 언어는 상관이 없다고 생각하기 쉽지만, 직접 경험해본 바로는 그렇지 않다. 특히 기초단계에서 언어 선택은 매우 중요하다. 언어의 특성은 알고리즘 전략의 선택지를 늘리기도 하고 좁히기도 하기 때문이다.

 

지난 세 달간 호기심이 생겨서 C언어를 공부하고 알고리즘 문제를 풀어보았다. ‘백준(프로그래밍 문제를 풀고, 온라인으로 채점 받는 사이트)’를 기준으로 브론즈에서 골드 난이도의 기초 문제들을 200문제 정도 풀어본 결과, C언어와 자바스크립트 두 가지 언어의 차이를 확실히 느낄 수 있었다.

 

C언어는 node.js에 비해 몇 가지 측면에서 훨씬 유리했고, 그동안 좀처럼 기초 수준을 벗어나지 못하던 알고리즘 실력을 빠르게 향상하는 데 도움이 되었다. 이번 글에서는 백준에서 두 가지 언어로 알고리즘 기초문제를 풀면서 느낀 점을 공유하고, 자바스크립트로 알고리즘을 공부하는 것의 한계에 관해 C언어와 비교해서 이야기해보겠다.

 

자바스크립트로 알고리즘을 공부하는 것의 한계

자바스크립트 언어로 알고리즘을 공부하기 시작한 지는 꽤 됐지만, 좀처럼 개념이 잡히지 않았고 실력이 잘 늘지 않았다. 지금 생각해보면 언어가 가진 특징이 원인이었다.

 

내장 객체와 메서드에 의존하게 된다

자바스크립트 내장 메서드
자바스크립트 배열의 내장 메서드 <출처: A List of JavaScript Array Methods>

 

알고리즘 공부를 시작하면 가장 처음 만나게 되는 객체는 문자열과 배열이다. 그리고 처음으로 구현하게 되는 알고리즘은 정렬과 탐색이다. 문제에서는 배열을 탐색해서 원하는 수를 찾아내거나, 오름차순 혹은 내림차순으로 정렬할 것을 요구한다. 이를 구현할 방법은 다양한데, 알고리즘을 무엇을 선택하는지에 따라 메모리를 더 많이 사용할 수도 있고, 소요 시간이 더 길 수도 있다. 문제의 제한조건을 만족하는 알고리즘 가운데 가장 좋은 성능을 내는 것을 선택하는 것이 문제풀이의 핵심이다.

 

C언어로 탐색 혹은 정렬을 구현하는 상황을 가정해보자. C언어에는 문자열이 따로 있지 않고, char 형 데이터들의 배열이 곧 문자열이다. 배열은 메모리공간에 정해진 크기만큼 일렬로 자리를 잡는데, 이 배열을 탐색하거나 정렬할 내장 메서드가 없으므로 탐색 함수와 정렬 함수를 직접 구현해야 한다.

 

대부분의 문제에서는 의도하고 있는 알고리즘이 있다. 의도된 알고리즘보다 성능이 나쁜 알고리즘을 선택하면 시간초과 혹은 메모리초과가 발생한다. 기본기를 다지는 과정에서는 문제를 보자마자 바로 적절한 알고리즘을 떠올리기는 어려우므로 다양한 알고리즘을 시도해보게 되고 그 과정에서 자연스럽게 여러 알고리즘의 장단점을 파악하게 된다.

 

하지만 자바스크립트로 구현한다면 상황은 달라진다. 자바스크립트 원형 객체들은 여러 편리한 기능과 속성을 내장 메서드와 프로퍼티로 이미 가지고 있다. 내장되어 있는 기능을 잘 사용하는 것은 분명 개발자의 훌륭한 자질이다. 하지만 다양한 알고리즘 전략을 숙지하고자 할 때는 언어의 편리함이 오히려 독이 된다.

 

자바스크립트 객체의 내장 메서드들은 이미 최적화되어 있어서 대부분의 문제에서 의도한 제약조건에 어긋나지 않는다. 또한 메서드 호출도 간결하기 때문에 코드 가독성이 높다. 따라서 자바스크립트 언어로 문제를 풀 때는 내장 메서드 호출이 곧 효율적인 풀이이며 고민의 여지가 별로 없다.

 

이것이 자바스크립트로 알고리즘 공부를 할 때 빠질 수 있는 악순환이다. 다양한 풀이방법에 대해 필사적으로 고민할 필요가 없기 때문에 내장 메서드를 사용해서 푸는 한 가지 방법밖에 모르게 되는 것이다. 매번 같은 방식을 이용하여 문제를 풀어나가는 과정을 알고리즘 공부라고 보기는 어렵다. 다양한 풀이를 고민하면서 문제에서 의도한 알고리즘이 무엇인지 고민해보는 과정이 진짜 공부이기 때문이다.

 

물론 실제로 서비스 기능을 구현할 때 다양한 알고리즘 이론을 활용할 일이 없을 수도 있다. 그러나 알고리즘 공부는 문제를 해결하는 능력을 훈련하는 과정이고, 하나의 문제를 풀어냈는지보다 그 문제를 풀기 위한 여러 가지 전략 중에서 올바른 선택을 했는지가 더 중요하다.

 

자료형이 엄밀하게 구분되지 않는다

대부분의 언어에서 변수의 자료형이 불변하는 것과 달리, 자바스크립트에는 변수의 타입이 중간에 변할 수 있다. 그뿐만 아니라 서로 다른 타입의 변수를 가지고 연산하는 것도 가능하며, 어떤 타입끼리 조합된 연산인가에 따라 연산자의 기능이 유연하게 바뀐다. 이러한 언어적인 유연성 때문에 개발자는 변수의 타입이 무엇인지 늘 생각하면서 작업해야만 한다. 변수의 자료형이 바뀌면 컴파일러가 경고하는 다른 언어들에 비해 개발자가 생각할 거리가 하나 더 있는 셈이다. 이는 자바스크립트 개발자라면 숙달되어야 하는 부분이지만, 알고리즘 문제에서 요구하는 본질과는 다소 거리가 있는 고민이라고 볼 수 있다.

 

자바스크립트의 타입은 가변적일 뿐 아니라 그 종류도 많지 않다. C언어를 비롯한 많은 언어에서 숫자가 int, double, float과 같은 자료형으로 나누어지는 것과 달리 자바스크립트에서는 숫자가 여러 범위로 구분되지 않는다. 대체로 number라는 하나의 타입만을 사용하고, 매우 큰 수를 표기하기 위한bigInt라는 타입이 있지만 number 타입의 범위가 충분히 커서 거의 사용할 일이 없다. 알고리즘 문제에서 자바스크립트를 사용하면 큰 수에 대한 예외 처리는 거의 고민할 일이 없는데, number라는 타입으로 2^53 - 1까지의 수를 안정적으로 표시할 수 있고 그보다 큰 수를 입력받는 경우는 드물기 때문이다.

 

입력값의 범위를 고민하지 않아도 된다는 점은 장점으로 볼 수도 있다. 하지만 메모리 자원을 고민해볼 기회가 없다는 측면에서 아쉽기도 하다. C언어에서는 각각의 변수에 담을 데이터의 범위를 고려해 필요한 만큼의 공간만 차지하는 자료형을 선택해 메모리 낭비를 막을 수 있다. 그에 비해 자바스크립트에는 그러한 선택권이 없기 때문에 상대적으로 로직의 성능저하가 발생할 수 있다.

 

c언어 정수 자료형
C언어에서 정수 자료형의 크기 및 범위 <출처: C 언어 코딩 도장>

 

자바스크립트 알고리즘 문제
2^53-1보다 큰 수를 number로 나타내는 경우와 bigint 로 나타내는 경우

 

의도치 않은 동작이 발생할 수 있다

컴파일러가 엄격하게 자료형을 검사해주는 C언어에 비해 자바스크립트는 의도와 다르게 동작할 가능성이 현저히 큰 언어이다.

 

실수하기 쉬운 경우 중 하나는 문자열과 숫자가 섞여서 덧셈연산 혹은 비교 연산이 되는 경우이다. 앞서 말했듯 node.js는 변수의 값이 숫자에서 문자로 바뀌거나 문자에서 배열로 바뀌는 등의 타입 변경이 발생해도 컴파일단계에서 오류로 판정하지 않는다. 게다가 숫자와 문자열은 둘 다 비교 연산 혹은 덧셈연산 구문에 들어갈 수 있고 연산자는 상황에 따라 기능이 유연하게 바뀐다.

 

숫자 간의 비교 연산은 대소비교로 동작하지만 문자열 간의 비교 연산은 알파벳 표기의 선후 비교로 동작한다. 만약 문자열과 숫자를 섞어서 비교 연산을 한다면 오류가 나지 않고, 문자열이 숫자로 묵시적으로 형변환되어 대소비교로 동작한다. 한편 덧셈연산자는 문자열 사이에 있을 때 두 문자열을 합쳐주는 기능으로 동작하고, 만약 문자열과 숫자가 섞여 있다면 이번에는 숫자가 문자열로 형변환된다. 이러한 유연성 덕분에 개발자의 의도와 다른 결과가 나올 가능성을 배제하기 어렵다.

 

자바스크립트의 슈퍼셋으로 타입스크립트가 등장한 맥락도 타입을 명시해서 의도치 않은 동작을 막고 로직의 예측 가능성을 향상하기 위함이다. 프로그래밍할 때 명시적인 자료형 혹은 타입의 존재는 생각할 단계를 하나 줄여준다. 컴파일러가 먼저 변수의 타입을 체크해줄 수 있다는 것은 큰 장점이다.

 

알고리즘의 본질은 문제를 해결하기에 적합한 알고리즘을 찾는 것이다. 의도치 않은 동작을 디버깅하는 시간은 알고리즘 문제의 본질과는 다소 거리가 멀다. 가장 이상적인 시나리오는 문제를 만났을 때 곧바로 필요한 알고리즘을 떠올리고 풀이 과정을 설계하여 시간 낭비 없이 코드를 작성하는 것이다. 그러나 문제의 의도와 무관하게 개발자의 실수로 발생하는 오류를 디버깅하는 시간이 길어지면 문제의 본질에서 멀어지게 된다. 의도치 않은 시간 소요가 번번이 발생한다면 문제를 푸는 일정한 리듬을 갖기가 어렵고 실력도 잘 늘지 않는다.

 

 

알고리즘 공부를 처음 시작한다면 C언어를 사용해보자

개인적으로 알고리즘은 항상 숙제처럼 여겨지는 영역이었다. 막상 시작했을 때도 실력이 쌓이는 것 같지 않아 많이 고민했었다. 그래서 자바스크립트 개발자로서 처음 C언어를 처음 접할 때 무척 어려웠다. 하지만 오히려 그 엄격함에 익숙해질수록 훨씬 직관적이고 쉬운 언어로 느껴졌다.

 

이번 기회에 C언어로 알고리즘 문제풀이를 해보면서 다양한 알고리즘을 비교하고, 메모리 공간효율을 고민해서 로직을 작성하는 경험도 해보았다. 또한 그동안 문법을 외워서 사용하던 자바스크립트의 내장 메서드의 존재 이유에 대해서도 생각하고 이해하는 계기가 되었다.

 

알고리즘 문제풀이의 가장 큰 장점은 논리 정연한 사고방식을 가질 수 있게 해준다는 것이다. 현실의 문제와 달리 알고리즘 문제는 명확한 해결 방법을 가지고 있고, 올바른 로직을 작성한다면 주어진 인풋에 대해 언제나 같은 결과가 도출된다. 문제풀이 능력을 잘 기르면 현실의 복잡한 문제를 대할 때도 다양한 변수들을 걷어내고 문제의 핵심을 찾아내 적절한 접근방법을 생각해내는 데 도움이 된다.

 

물론 알고리즘 난이도가 올라간다면 C언어를 고집하긴 어려울 것 같다는 생각도 든다. 이미 C++는 C언어에 여러 가지 유용한 기능들을 추가해 지원해주고 있다. 무엇보다 개발자에게는 내장 메서드를 적절하게 활용할 수 있는 능력이 중요한 능력 중 하나이다. 따라서 너무 많은 함수를 일일이 만드는 것보다는 내장 메서드를 잘 활용하는 것이 효율적인 풀이라고 생각한다. 하지만 적어도 알고리즘 기본기를 쌓는 단계에서는 C언어의 불편함은 오히려 도움이 된다. 한 번쯤 도전해보는 것을 추천한다.

 

요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.

댓글 0

zwoo

2년차 프론트엔드 개발자입니다. 리액트와 자바스크립트를 좋아합니다.

같은 분야를 다룬 글들을 권해드려요.

요즘 인기있는 이야기들을 권해드려요.

일주일에 한 번!
전문가들의 IT 이야기를 전달해드려요.

[구독하기] 버튼을 누르면 개인정보 처리방침에 동의됩니다.

일주일에 한 번! 전문가들의 요즘IT 이야기를 전달해드려요.

[구독하기] 버튼을 누르면 개인정보 처리방침에 동의됩니다.