📚 프로그래밍 언어란?
프로그래밍 언어는 컴퓨터(기계)에게 명령이나 연산을 시키기 위해 사용되는 언어입니다.
일반적으로는 소프트웨어를 작성하기 위한 소프트웨어를 가리키며 고급 언어일수록 자연어에 가깝습니다.
프로그래밍 언어는 다양한 방식으로 분류될 수 있습니다.
📗 수준에 따른 분류
인간 친화도로 표현할 수 있으며 크게 3가지로 나눌 수 있습니다.
- 저급 언어 (Low Level)
- 기계어 (Machine Code)
- 어셈블리어 (Assembly language)
- 고급 언어 (High Level)
❗ 저급, 고급은 성능 차이가 아닌 추상화 또는 인간 친화도 차이입니다.
기계어
컴퓨터가 별다른 해석 없이 읽을 수 있는 프로그래밍 언어입니다.
인간의 입장에서는 난해한 프로그래밍 언어이지만 CPU를 직접 제어하며 컴퓨터만을 위해 사용되는 언어입니다.
CPU는 이진수 데이터를 다룰 수 있는 능력을 가지고 있어서 1과 0으로 이루어진 기계어 명령어를 읽을 수 있습니다.
다른 언어들은 컴퓨터가 이해할 수 있는 기계어로 번역되어 전달되며, 쉽게 말해 모든 프로그래밍 언어의 종착지입니다.
ex) 001001 11101 11101 1111111111111000
컴퓨터가 별다른 번역 없이 바로 사용할 수 있어서 빠르지만 사람이 이해할 수 없어 기계어로 코드를 작성하는 경우는 거의 없습니다.
어셈블리어
프로그래밍 언어의 하나로 기계어에서 한 단계 위의 언어입니다. 기계어와 단 둘 뿐인 저급 언어에 속합니다.
기계어는 컴퓨터 관점에서 바로 읽을 수 있지만 사람이 사용하기 불편한 언어이기에 어셈블리어가 등장했습니다.
기계어와 어셈블리 명령어가 대부분 1:1로 대응이 되고 어셈블러를 통해 기계어로 변환됩니다.
이러한 이유 때문에 어셈블리어는 고급 언어와 기계어 사이에 있다 하여 중간 언어라고도 불립니다.
어셈블리어는 기계어 대비 생산성이 높아졌지만 고급 언어에 비하면 생산성이 매우 떨어집니다.
고급 언어
사람이 이해하기 쉽게 작성된 프로그래밍 언어로 저급 프로그래밍 언어보다 가독성이 높고 다루기 간단하다는 장점이 있습니다.
컴파일러나 인터프리터에 의해 저급 프로그래밍 언어로 번역되어 실행되며, 프로그래밍 언어가 높은 수준인지는 추상화의 정도에 따라 정의됩니다.
대부분의 언어는 고급 언어로 분류되며 다음으로 소개될 분류 방법도 고급 언어를 분류했습니다.
📗 해석 방식에 따른 분류
크게 AOT 컴파일 언어, JIT 컴파일 언어, 그리고 인터프리터 언어로 분류할 수 있습니다.
이 분류는 정확히 말하면 언어의 분류가 아니라 언어 구현체의 분류로, 언어 명세가 어느 한쪽을 완전히 배제한 형태가 아니거나 다른 특별한 이유가 있지 않는 이상 대부분의 언어는 이론상 어떤 식으로든 구현 가능합니다.
산업 현장에서 널리 쓰이는 언어는 기본적으로 컴파일되어 실행된다고 보면 된다. 아래의 구분은 언어의 대표 구현체를 따른 것이고, 결코 언어 자체의 분류가 아님에 유의할 것. 예를 들면, C언어라고 항상 AOT 컴파일되는 것이 아니며[10], Java라고 항상 JIT 컴파일되는 것도 아니며[11], OCaml[12]과 Erlang[13]등의 언어는 아예 기본 구현체에 둘 다 포함시켜서 배포한다.
- 나무위키 프로그래밍 언어 -
🌱 AOT(Ahead Of Time) 컴파일 언어
C언어, C++, Rust, 파스칼, Haskell 등이 포함된다. 소스 코드를 미리 기계어로 번역[14]해서 실행한다. 미리 컴파일하더라도 가비지 컬렉션의 유무에 따라 실행 속도가 차이가 나기도 한다.
AOT 컴파일(ahead-of-time compile)은 목표 시스템의 기계어와 무관하게 중간 언어 형태로 배포된 후 목표 시스템에서 인터프리터나 JIT 컴파일 등 기계어 번역을 통해 실행되는 중간 언어를 미리 목표 시스템에 맞는 기계어로 번역하는 방식을 지칭한다.
컴파일 시 오류가 생기면 다시 전체 코드를 컴파일해야 하지만 컴파일이 완료된다면 빠른 성능을 기대할 수 있다.
🌱 JIT 컴파일 언어
대부분의 언어가 여기에 속한다. Java 및 JVM 언어, JavaScript 및 WebAssembly, C# 및 CLR 언어 등의 언어들은 기본 구현체가 JIT 컴파일러이다.
이 언어들은 대부분 바이트코드를 생성하는 1차 컴파일러, 그리고 1차 컴파일러가 생성한 바이트코드를 실행하는 가상머신(VM)으로 이루어져 있다.
JIT 컴파일(just-in-time compilation) 또는 동적 번역(dynamic translation)은 프로그램을 실제 실행하는 시점에 기계어로 번역하는 컴파일 기법이다.
전통적인 입장에서 컴퓨터 프로그램을 만드는 방법은 두 가지가 있는데, 인터프리트 방식과 정적 컴파일 방식으로 나눌 수 있다. 이 중 인터프리트 방식은 실행 중 프로그래밍 언어를 읽어가면서 해당 기능에 대응하는 기계어 코드를 실행하며, 반면 정적 컴파일은 실행하기 전에 프로그램 코드를 기계어로 번역한다.
JIT 컴파일러는 두 가지의 방식을 혼합한 방식으로 생각할 수 있는데, 실행 시점에서 인터프리트 방식으로 기계어 코드를 생성하면서 그 코드를 캐싱하여, 같은 함수가 여러 번 불릴 때 매번 기계어 코드를 생성하는 것을 방지한다.
🌱 인터프리터 언어
소스 코드를 한 줄 한 줄 읽어 그때그때마다 번역해서 수행한다.
BASIC이나 bash 같은 언어가 이런 형태이다.
소스 코드를 한 줄씩 읽어서 수행하기 때문에 대체로 AOT 컴파일 언어나 JIT 컴파일 언어에 비해 성능이 떨어지는데 2010년대 들어서 인터프리터엔진에도 JIT기법이 도입되었다.
Python, Ruby, PHP 등이 있다.
장점은 구현이 단순하고 구현체의 절대적인 크기가 작다는 것이다.
인터프리터(interpreter)는 프로그래밍 언어의 소스 코드를 바로 실행하는 컴퓨터 프로그램 또는 환경을 말한다.
인터프리터는 다음의 과정 가운데 적어도 한 가지 기능을 가진 프로그램이다.
- 소스 코드를 직접 실행한다.
- 소스 코드를 효율적인 다른 중간 코드로 변환하고, 변환한 것을 바로 실행한다
- 인터프리터 시스템의 일부인 컴파일러가 만든, 미리 컴파일된[1] 저장 코드의 실행을 호출한다.
❗ 컴파일러 vs 인터프리터
인터프리터는 고급 언어로 작성된 원시코드 명령어들을 한번에 한 줄씩 읽어들여서 실행하는 프로그램이다.
고급언어로 작성된 프로그램들을 실행하는 데에는 두 가지 방법이 있다.
가장 일반적인 방법은 프로그램을 컴파일 하는 것이고, 다른 하나는 프로그램을 인터프리터에 통과시키는 방법이다.
인터프리터는 고급 명령어들을 중간 형태로 번역한 다음, 그것을 실행한다. 이와는 대조적으로, 컴파일러는 고급 명령어들을 직접 기계어로 번역한다.
컴파일된 프로그램들은 일반적으로 인터프리터를 이용해 실행시키는 것보다 더 빠르게 실행된다. 그러나 인터프리터의 장점은 기계어 명령어들이 만들어지는 컴파일 단계를 거칠 필요가 없다는데 있다. 컴파일 과정은 만약 원시 프로그램의 크기가 크다면, 상당한 시간이 걸릴 수 있다. 이와는 달리 인터프리터는 고급 프로그램을 즉시 실행시킬 수 있다. 이런 이유 때문에, 인터프리터는 종종 프로그램의 개발단계에서 사용되는데, 그것은 프로그래머가 한번에 적은 양의 내용을 추가하고 그것을 빠르게 테스트 해보길 원하기 때문이다. 이 외에도 인터프리터를 이용하면 프로그래밍을 대화식으로 할 수 있기 때문에, 학생들의 교육용으로 사용되는 경우도 많다.
📗 메모리 관리 방식에 따른 분류
크게 비관리 언어와 관리 언어로 분류할 수 있다.
비관리 언어(Unmanaged Language)
동적 할당된 메모리의 해제가 자동으로 이루어지지 않으며, (주로 포인터를 이용한) 메모리 직접 접근이 가능한 언어이다. Bare Metal Language라고도 하며, 대표적으로는 C, C++ 등이 있다. 수동 기계제어가 가능한 만큼 프로그래머의 최적화 실력에 따라 높은 퍼포먼스를 낼 수 있지만, 반대로 생각하면 그렇기 때문에 메모리 관리를 제대로 하지 않을 시 프로그램이 망가질 가능성이 커서 관리 언어에 비하면 생산성은 떨어진다.
관리 언어(Managed Language)
동적 할당된 메모리의 해제가 가비지 컬렉터(Garbage Collector)에 의해 자동으로 이루어지며, 메모리 접근에 대한 부분을 추상화시켜 직접적인 기계제어를 기본적으로 차단한다. 대표적으로는 Java, C#, Python, Go 등이 있다. 메모리 관리를 언어 차원에서 책임져 주기 때문에 상대적으로 생산성은 높지만, 메모리에 직접 액세스할 수 없으므로 비관리 언어에 비해 성능 면에서 손실이 있다.
📗 정적 타입, 동적 타입
정적 타입 언어
자료형(Type)이 고정돼 있는 언어. 간단히 얘기하자면 정수형으로 정의한 1은 계속 정수형 1로 남아있다. 이걸 실수형 1.0으로 바꾸려면 명시적인 형 변환(Type casting)을 해줘야 한다. 묵시적 형 변환이 이루어지는 경우도 있지만 제한적이다. C, C++, Java, C# 등이 여기에 해당된다.
동적 타입 언어
타입이 실행 시간에 결정되는 언어. 실행하기 전까지는 특정 식의 타입을 알 수 없다. 자료형이 그것을 처리할 함수(또는 메서드)에 따라 그때그때 바뀌는 언어. 예를 들어 정수형 1을 정의했어도 그걸 처리할 함수가 문자열을 받아들이게 설계돼있다면 자동으로 정수형 1을 문자 1로 바꿔준다. Python, JavaScript, Ruby 등이 여기에 해당된다.
https://namu.wiki/w/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%20%EC%96%B8%EC%96%B4#s-4.4
동적 타입과 정적 타입의 차이는 타입이 컴파일 타임에 결정되느냐 실행시간에 결정되느냐 뿐이다. 파이썬도 개별 값들은 내부적으로 고정된 타입을 가지며 암시적으로 마구 변환되지 않는다. 다만 명시적인 조건 없이도 공통의 인터페이스를 구현하는 것들을 넣어주면 동작하게 되어 있다. 이런 개념은 "덕-타이핑"이라고 하고 동적 타입만의 특성은 아니며 정적 타입 언어에서도 구현이 가능하다. 단지 정적 타입 언어들이 추구하는 방향이 덕 타이핑과 맞지 않는 경우가 많기 때문에 잘 안쓸 뿐이다.
동적 타입 언어 중에서 자바스크립트는 암시적으로 자료형을 마구 변환해주지만 기능보다는 설계결함으로 간주된다. 정적 타입 언어 중에서는 C가 이런 무분별한 암시적 형변환으로 악명높다. 알아서 변환해준다는게 좋게들릴지 몰라도 막상 써보면 괴로워지는 경우가 더 많다.
정적 타입 언어가 별 거 아닌 것처럼 느껴질 수도 있지만 실은 프로그래머들을 짜증나게 하는 주범이 바로 형 변환(Type casting)이기 때문에 동적 타입 언어는 이런 점에서 매우 강점을 가진다. 특히 객체 지향 언어에서는 동적 타입 및 그것의 일반화버전이라 할 수 있는 덕 타이핑(Duck typing)이 프로그래머에게 수많은 혜택을 준다. 예를 들어 오리라는 타입과 닭이라는 타입이 있고 둘 다 날아오르는 기능이 있다면 정적 타입 언어에서는 상위 인터페이스를 추출하는 등의 부가 작업이 필요한데 덕 타이핑을 지원하는 언어에서는 그냥 넣어버리면 알아서 난다. 물론 단점도 있는데 고래 같이 못 나는 타입을 집어넣으면 실행시간 오류(런타임 에러)를 뱉어버린다는 것. 정적 타입 언어는 이런 문제가 없다...고 알려져 있지만 거짓말이고 정적 타입 언어도 닭은 닭인데 통닭 같이 못 나는 타입을 집어넣는 바람에(기술적으로는 해당 메서드가 구현이 안된 객체) 런타임 에러가 나올 수 있다.
그러나 이건 현실을 전부 고려하지 않은 반쪽짜리 시각이고, 요즘은 정적 타입 언어가 동적 타입 언어보다 훨씬 생산적이고 오류가 날 가능성을 줄여준다는 점이 정설로 굳어지고 있다. 당장 동적 타입 언어로 유명한 파이썬과 자바스크립트[22]가 정적 타입으로 옮겨가려는 움직임을 보이는 것으로 알 수 있지 않을까? 위에 있는 예시는 거의 모두 현실과는 동떨어진 예제와 설명이다. 위에서 든 상위 인터페이스 추출이나 메서드 미구현 문제는 정적 타입의 문제가 아니라 객체지향의 문제로, 정적 타입과 하등 상관없고 오히려 동적 타입으로 객체지향 개발을 하려고 할 때 더 흔히 일어나는 문제들이다. 강하고 안전한 정적 타입 시스템을 지원하는 언어는 대부분의 일상적 프로그래밍 오류를 미연에 방지해준다. 가령 가장 기본적인 동작인 함수 호출의 사례만 봐도 이러한 측면을 쉽게 이해할 수 있다. 동적 타입 언어는 그 어떤 쓰레기 값을 인자로 사용해서 호출해도, 심지어 아예 인자의 갯수마저 틀려버려도 그 호출 코드를 런타임에 아무 의심없이 실행해버리고 예상치 못한 동작을 하며 디버깅에 애를 먹이기 일쑤다. 하지만 정적 타입 언어는 잘못된 호출을 하고 있다고 컴파일 시에 오류를 친절히 알려준다[23]. 하스켈을 비롯한 강-정적타입 언어 사용자들이 "컴파일이 된다면 버그는 없다"고 하는 말이 빈 말이 아닌 것이다.
📗 프로그래밍 패러다임
절차적 언어
알고리즘과 로직에 의거하여 단계 단계 밟아가며 문제를 해결하도록 짜는 프로그래밍 언어. 대표적인 언어는 C와 Pascal. 종종 객체지향 언어의 상대적 개념으로 절차지향 언어란 말을 쓰기도 하는데, 이는 객체지향 언어가 등장한 이후에 나타난 것으로 보인다.
객체 지향 언어
객체 지향 언어는 프로그래밍을 함에 있어서 데이터와 그 데이터를 처리할 메소드를 한데 묶어 객체를 만들고 객체들을 조립하는 것을 목표로 한 언어들을 말한다.
객체 지향 언어의 특징은 추상화, 캡슐화, 상속성, 다형성이 있다.
추상화는 외부 인터페이스만 제공하고 객체 내부를 숨겨서 어떻게 일을 하는지 몰라도 결과를 내보낸다.
캡슐화는 객체 내부에 필요한 데이터등을 묶어서 한번에 관리 할 수 있게 해준다.
상속은 모객체를 상속받아 추가 기능을 더 붙이거나 약간의 수정을 가한 객체를 만들 수 있다.
다형성은 메소드 이름은 같더라도 매개변수의 유무, 매개변수의 개수, 매개변수의 자료형, 반환하는 값의 자료형에 따라 다른 메소드가 실행될 수 있다는 것이다. 즉, 메소드명이 같아도 실제 행위는 타입에 따라 다를 수 있다는 것이며, 상향 형변환(upcasting) 및 동적 바인딩이라는 개념과 관련이 있는 특성이다.
(객체 지향 5대원칙과 4가지 특성에 대해서는 따로 포스팅을 할 예정이다.)
선언형 언어
선언형 언어(Declarative language)는 명령형 언어와 대비되는 개념으로, 함수형 언어와 논리 프로그래밍(Logic programming)등이 여기에 속한다. 현재 학계를 떠나 슬슬 업계 전반으로 확산되고 있다. 특히 신기술의 도입이 빠른 웹 앱 계열에서 선언형 스타일의 프로그램이 선호되고 있는데 JavaScript 코드의 코딩 스타일이 점점 선언형으로 변화하고 있다. 선언형으로 기술하는 가장 유명한 라이브러리로는 제이쿼리(jQuery)가 있으며 앵귤러JS(AngularJS)는 선언형 언어의 가장 최신 트렌드인 반응형 프로그래밍 개념을 도입하고 있다.
📗 특수 목적 언어
-
SQL - 관계형 데이터베이스에서 데이터를 조작하기 위해 사용되는 표준 언어로 여러 DBMS에서 지원한다.
-
CUDA - 엔비디아제 GPU를 제어하는 언어.
-
Prolog - 인공지능 언어
-
Processing - 그래픽스 처리를 위한 언어
-
MATLAB - 공학용 시뮬레이션용 언어
-
PHP - 웹 사이트를 만드는데 특화된 언어
-
Verilog, VHDL - 디지털 하드웨어 설계를 위한 언어로, 반응형 프로그래밍에 속한다.
-
Verilog-A - 아날로그 하드웨어 설계를 위한 언어
📖 레퍼런스
https://namu.wiki/w/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%20%EC%96%B8%EC%96%B4
hhttps://ko.wikipedia.org/wiki/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D_%EC%96%B8%EC%96%B4
https://kadensungbincho.tistory.com/81?category=910877
'코딩 > Computer Science' 카테고리의 다른 글
[네트워크] 네트워크란? (0) | 2022.04.26 |
---|