[!attention] 이 장의 목표
- Fortran에 대한 이해 — 현대 Fortran의 역사와 현재 동향을 배운다.
- 실행의 큰 그림 — 소스 코드를 컴퓨터가 어떻게 실행하는지 이해한다.
- 컴파일·실행 — 구글 코랩에 gfortran을 설치하고 프로그램을 컴파일하고 실행한다.
이 장이 끝나면 다음 질문들에 답할 수 있다.
왜 하필 Fortran인가? 1957년에 태어난 언어가 어떻게 2024년에 인기 순위를 다시 끌어올렸을까? 달 탐사의 궤적 계산, 일기예보, 슈퍼컴퓨터 시뮬레이션은 어떤 언어로 돌아갈까?
직접 빠르게 코드를 작성하고 실행해 볼 수 있을까? 컴파일러를 내 컴퓨터에 설치하지 않고도, 브라우저만으로 Fortran 프로그램을 작성·실행해 결과를 볼 수 있을까?
출력된 숫자들을 시각화할 수 있을까? Fortran이 계산한 결과를 한눈에 들어오는 그래프로 표현할 수 있을까?
[!important] 1.1 컴퓨터는 어떻게 명령을 수행하는가?
Fortran 언어를 소개하기 전에 컴퓨터 시스템의 작동 원리를 알아보자. 프로그래밍 소스(source) 코드 작성에서 결과를 출력하기까지, 컴퓨터 내부에서 무슨 일이 일어지는지를 먼저 이해하면 디지털 리터러시(digital literacy)를 익히는 데 도움이 된다.
실리콘은 원래 전기가 잘 통하지 않는 부도체에 가깝다. 하지만 여기에 특정 불순물을 아주 살짝 섞어주는 도핑(doping) 기술을 사용하면, 전자가 남아도는 n형 반도체와 전자가 모자라는 p형 반도체가 만들어진다. 이 두 반도체를 서로 접합하면(p-n 접합), 전기를 특정 방향으로만 흐르도록 통제할 수 있는 구조를 만들 수 있다. 이를 바탕으로 탄생한 것이 트랜지스터이다.
트랜지스터는 아주 작은 전자 스위치로 이해하면 된다. 우리가 손으로 벽면 스위치를 눌러 불을 켜듯, 트랜지스터는 게이트(gate)라는 길목에 전압을 걸어주거나 차단하는 방식으로 전류의 흐름을 통제한다.
디지털 세계에서는 전압이 높을 때를 숫자 1, 전압이 낮거나 없을 때를 숫자 0으로 약속한다. 트랜지스터 스위치를 어떻게 조립하느냐에 따라 이 0과 1을 가지고 수학적인 계산을 할 수 있다. 이를 논리 게이트(logic gate) 라고 하며 다음과 같은 종류가 있다.
기본 게이트를 만든 뒤, 이 게이트들을 다시 조립해 더 큰 기능을 가진 장치를 만들 수 있다. 예를 들어, 2진수 덧셈을 수행하는 '덧셈기(가산기)'를 만들 수 있고, 계산된 결과를 다음 연산을 위해 잠시 기억하거나 저장해두는 '레지스터(register)'나 '메모리(memory)' 같은 기억 장치도 만들 수 있다. 이런 부품을 촘촘히 모아 컴퓨터의 두뇌인 중앙처리장치(CPU)를 완성할 수 있다.
최신 컴퓨터 칩 하나에는 수백억 개의 트랜지스터가 집적된다. 인간의 두뇌로는 수백억 개의 물리적 스위치가 켜지고 꺼지는 개별적인 상태를 일일이 통제하고 설계하는 것이 불가능하다. 하지만 추상화 계층(abstraction layer)이라는 강력한 전산학적 개념을 도입하여 이 거대한 복잡성을 제어할 수 있다. 추상화 계층이란 복잡한 내부 물리적 원리와 세부 사항은 아래층에 숨겨두고(캡슐화, encapsulation), 위층에는 기능과 인터페이스(interface)만 노출해서 부품처럼 가져다 쓸 수 있도록 한 구조이다.
한 줄의 과학 계산 코드가 하드웨어에서 실행되기까지, 컴퓨터는 아래 표와 같이 거대한 추상화의 단계들을 밟아 내려간다.
엔지니어가 컴퓨터 칩을 설계할 때, 수백억 개의 트랜지스터 위치를 일일이 손으로 그리지 않는다. "내부적으로 어떻게 작동하는지"는 하위 계층에 맡겨두고, "이 블록은 덧셈을 하는 부품이다"라는 기능만 믿고 탑을 쌓아 올리듯 설계한다. 현대에는 이 과정을 마치 컴퓨터 프로그래밍을 하듯 하드웨어 기술 언어(Verilog, VHDL)로 코드를 작성하면, 전문 자동화 프로그램이 이 코드를 읽어 복잡한 반도체 회로도로 변환한다.
프로그래밍 소스 코드가 컴퓨터에서 실행되어 결과를 내는 과정은, 단단하고 정교하게 쌓아 올린 반도체 계층 구조 위에서 소프트웨어와 하드웨어가 완벽하게 맞물려 돌아가는 것이다.
계층 (Layer) |
담당 역할 및 물리적 실체 | 추상화의 이점 |
|---|---|---|
| 최상위 소프트웨어 (Fortran / Python) |
수식을 직관적인 영문자와 기호로 표현하는 단계 (예: y = sin(x)) |
하드웨어 메모리 주소나 CPU 레지스터를 몰라도 물리 방정식을 그대로 타이핑하여 연산할 수 있다. |
| 컴파일러 / 어셈블리어 (Compiler / Assembly) |
고차원 언어를 특정 CPU 아키텍처에 종속적인 기계 부호 명령어로 1대1 번역하는 연결 고리. | 사람이 이해하는 기호 표기를 이진 기계어 바이트 단위로 번역하여 소프트웨어와 하드웨어 사이의 언어적 간극을 메운다. |
| 기계어 및 ISA (Machine Code & ISA) |
CPU가 직접 해독할 수 있는 $0$과 $1$의 이진 비트 명령어와 하드웨어 명령어 집합 규격(Instruction Set Architecture) | 가상 소프트웨어 세계의 명령 체계가 물리적 반도체 칩이 이해할 수 있는 전기적 제어 신호이다. |
| 디지털 논리 회로 (Digital Logic Circuits) |
AND, OR, NOT 등의 기본 논리 게이트와 이들이 조합된 연산 장치(ALU), 레지스터들의 물리적 집합체 | 전류의 물리 흐름을 무시하고, '전압이 높으면 1(True), 낮으면 0(False)'이라는 이진 논리 부품으로 CPU를 조립한다. |
| 물리적 트랜지스터 (Transistor / Physics) |
실리콘 웨이퍼 위에 미세 공정으로 새겨진 수백억 개의 전류 제어 스위치 소자이자, 양자역학적 물리 법칙이 지배하는 미시 세계 | 실리콘 원자 내부의 전하 이동이나 도핑 농도 같은 극미 물리 현상을 on/off 디지털 스위치 기능 하나로 완벽히 추상화한다. |
[!important] 1.2 컴퓨터 언어의 진화와 Fortran의 탄생
컴퓨터 안에서 소프트웨어가 작동하는 원리는 생각보다 단순하다. 소프트웨어는 두 가지 핵심 하드웨어인 중앙처리장치(CPU)와 기억장치(memory)를 제어하는 명령서다.
CPU (중앙처리장치): 오직 연산만을 위해 존재하는 강력한 계산기이다. 스스로 데이터를 오래 기억하지 못하므로, 끊임없이 메모리로부터 데이터를 받아와 계산을 수행한다.
메모리 (기억장치): CPU가 바로 꺼내서 쓸 수 있도록 데이터를 일시적으로 보관하는 창고(공간)이다. 수식의 입력값과 연산이 끝난 결과값 모두 이 창고에 저장된다.
우리가 작성한 코드는 메모리 창고에서 pi와 radius 값을 꺼내 CPU 계산기에 넘겨주고, CPU가 곱셈을 끝내면 그 결과물을 다시 메모리의 area라는 빈 칸에 집어넣으라는 명령으로 번역되어 수행된다.
컴퓨터가 알아듣는 언어가 오직 0과 1로 이루어진 '기계어(machine language)'뿐이다. 초창기 프로그래머들은 이 0과 1을 직접 타이핑하거나, 이를 기호로 아주 조금 알아보기 쉽게 바꾼 저급 언어(low-level language)인 어셈블리어(Assembly language)로 코딩했다. 짧은 수식 하나를 계산하려 해도 수십 줄의 명령을 복잡하게 적어야 했고, 점 하나만 잘못 찍어도 밤을 새워 버그를 찾아야 했다. 인간의 생각 방식과 기계의 언어 사이의 간극이 상당히 컸던 시대였다.
이 간극을 좁히기 위해 등장한 것이 바로 고급 언어(high-level language)다. "인간이 수학 공식이나 일상 언어와 비슷한 형태로 코드를 짜면, 중간에서 누군가 이를 기계어로 번역해 주면 되지 않을까?"라는 획기적인 아이디어에서 출발했다. 그리고 이 어려운 번역을 대신 해주는 프로그램을 컴파일러(compiler)라고 부른다.
이러한 배경 속에서 역사상 최초로 널리 쓰인 고급 프로그래밍 언어가 탄생했는데, 그것이 바로 FORTRAN이다. 이름 자체가 수식 번역(FORmula TRANslation)의 앞 글자에서 따온 만큼, 철저하게 과학 기술 계산을 위해 태어났다.
1953년 IBM의 John Backus는 프로그래밍의 엄청난 시간과 노력의 비용을 줄이자는 제안서를 냈고, 1957년 최초의 FORTRAN 컴파일러가 세상에 나왔다. 당시에는 키보드와 모니터도 없이 종이 카드에 구멍을 뚫어(천공카드) 프로그램을 입력하던 열악한 시절이었지만, FORTRAN의 파급력은 대단했다.
"사람이 읽고 이해할 수 있는 코드를 작성한다"는, 오늘날 우리가 당연하게 여기는 상식이 바로 FORTRAN에서 시작되었다. 이후 Fortran은 함수나 서브루틴 같은 현대적 개념들을 흡수하며 발전했고, 수십년간 과학·공학계의 절대적인 표준 도구로 자리 잡았다.
1960년대 NASA의 아폴로 계획(Apollo Program)에서 FORTRAN은 그 진가를 가감 없이 발휘했다. 우주선을 지구에서 달까지 보내고 다시 무사히 귀환시키는 작업은 극도로 정밀한 궤도 및 궤적 계산, 연료 소모량 시뮬레이션이 필수적이었다. 이 엄청난 양의 계산을 수행한 지상의 IBM 대형 컴퓨터(메인프레임) 시스템을 움직인 주역이 바로 FORTRAN이었다.
영화 《히든 피겨스》의 주인공으로 잘 알려진 NASA의 Dorothy Vaughan은 전자식 컴퓨터 시대의 도래를 직감하고 독학으로 FORTRAN을 마스터하고 팀원들을 전문 프로그래머로 재교육했다. 아폴로 우주선 내부(AGC)의 극도로 제한된 메모리에는 가벼운 어셈블리어가 탑재되었지만, 지상에서 정밀한 궤도 계산과 연료 소모량 시뮬레이션을 수행하며 우주선의 임무를 설계한 메인 프레임 시스템은 FORTRAN으로 구동되었다. 즉, FORTRAN은 인류에게 달로 가는 길을 계산해 준 언어였다.
과학 기술 계산에 철저히 최적화된 FORTRAN은 오늘날에도 NASA Johnson Space Center에서 우주선의 궤적 최적화 프로그램인 Copernicus를 구동하는 데 실제로 활용되며 그 독보적인 성능을 입증하고 있다.
[!important] 1.3 레거시 FORTRAN에서 현대 Fortran으로
FORTRAN은 국제 표준(ISO/IEC)으로 관리되며, 그 표준은 꾸준히 개정되어 왔다. FORTRAN 표준의 역사적 변천 과정과 핵심 변화는 다음과 같다.
표준 |
연도 | 핵심 변화 |
|---|---|---|
| FORTRAN 66 | 1966 | 최초의 공식 표준. 파편화된 언어 사양을 하나로 규격화 |
| FORTRAN 77 | 1978 | 블록 if, 문자형(character) 도입. 고정형식 |
| Fortran 90 | 1991 | 자유형식, 모듈, 배열 연산, 동적 할당, 파생형. FORTRAN에서 Fortran으로 명칭 변경 |
| Fortran 95 | 1997 | 소규모 개선(forall, pure, elemental 도입) |
| Fortran 2003 | 2004 | 객체지향 지원, C와 상호 운용성, IEEE 부동 소수점 표준 반영 |
| Fortran 2008 | 2010 | do concurrent, block, 서브모듈, 코어레이(coarrays) 도입 |
| Fortran 2018 | 2018 | 병렬 기능 강화, C 상호 운용성 개선 |
| Fortran 2023 | 2023 | 최신 표준. 여러 편의 기능 추가, 문법적 보완 |
흔히 말하는 현대 Fortran 은 보통 Fortran 90 이후의 표준을 가리킨다. 현대 Fortran은 다음과 같은 스타일을 지향한다.
자유 형식(free form)의 전면 도입: 타자기와 천공카드 시절의 유물인 엄격한 칸수 제약에서 완전히 벗어나, 현대적인 가독성과 개발 유연성을 갖춘 코드를 짜기 편한 환경을 제공한다.
implicit none 선언 의무화: 모든 변수를 사용하기 전에 명시적으로 선언하도록 강제함으로써, 과거 FORTRAN의 고질적인 문제였던 변수명 오타로 인한 황당한 버그를 컴파일 단계에서 원천 차단한다.
배열 통째 연산(array syntax): 번거로운 다중 루프를 일일이 돌리지 않고 배열이나 행렬을 하나의 덩어리로 직접 연산하여, 현대 CPU의 병렬 처리 및 벡터화 성능을 최대로 끌어올린다.
모듈(module)을 통한 구조화: 기능별로 코드를 분리 및 독립화하여 복잡한 대규모 공학 계산 프로젝트에서도 안전하게 데이터를 공유하고 인터페이스를 관리한다.
FORTRAN 77 이전에 쓰이던 고정 형식이나 GOTO 문 중심의 스파게티 코드는 이 책에서 래거시 코드를 읽고 해석하기 위한 최소 지식으로만 가볍게 다룬다. 본문의 모든 실습과 예제는 처음부터 현대적인 코딩 습관을 들일 수 있도록 현대 Fortran 표준을 기준으로 진행한다.
[!note] [알아두기] 이 책에서는 왜 최신 2023이 아니라 2018을 기준으로 삼는가?
현대 Fortran의 사실상의 필수 요소는 이미 2008에 다 들어 있다. 모듈, 동적 배열(
allocatable), 파생형,block, 서브모듈,do concurrent까지 입문 과정에서 가르칠 현대적 기능은 2008만으로 충분하다. 2018나 2023 표준에 더해진 고급 병렬화·C 상호 운용성의 세부 등은 입문 범위를 넘고 컴파일러 지원도 충분하지 않은데, 컴파일러가 그 기능을 따라 구현하기까지는 시간이 걸리기 때문이다.이 책에서 사용하려는 코랩 기반의 개발 환경에서 기본으로 지원하는 gfortran 11은 Fortran 2008 표준의 대부분과 Fortran 2018의 일부 기능 정도를 지원한다. gfortran 11은
-std=f2018은 받아들이지만-std=f2023은 아직 인식하지 못한다. 따라서, 이 책을 쓰는 시점에서 사용가능한 최신 표준인 2018을 채택하여 모든 예제에 적용하였다.
오늘날 컴퓨터는 웹 개발, 영상 추천, 음성 인식 등 정보 처리의 스펙트럼이 무한히 확장된 시대를 지나고 있다. 각 도메인마다 최적화된 언어가 달라 웹 프론트엔드는 JavaScript, 범용 데이터 과학은 파이썬, 시스템 및 하드웨어 제어는 C가 지배하고 있다.
이 넓은 소프트웨어 생태계 속에서 Fortran이 선점한 자리는 명확하다. 바로 '극한의 수치 계산(extreme scientific computing)' 그 자체다.
기상·대기 역학 시뮬레이션, 양자 화학 전자 구조 계산, 대규모 구조 해석 등 거대한 행렬 대수와 부동소수점 반복 연산이 지배하는 물리 및 공학 도메인에서 Fortran은 수십 년간 독보적인 대체 불가능성을 증명해 왔다.
Fortran과 대표적인 타언어와으 전산 성능을 아래에 대조하였다.
시스템 언어 C와 비교했을 때, C는 운영체제 개발이나 하드웨어 레지스터의 직접 제어 같은 시스템 프로그래밍 영역에 최적화되어 있다. 그러나 과학 계산 관점에서는 포인터 에일리어싱(pointer aliasing, 서로 다른 포인터가 동일한 메모리를 가리켜 컴파일러의 루프 최적화를 방해하는 현상)으로 인해 성능 병목이 발생하기 쉬우며, 개발자가 포인터와 메모리를 직접 관리해야 하는 오버헤드가 크다. 반면 Fortran은 다차원 배열 연산이 언어 자체에 내장되어 있어 컴파일러가 극도의 루프 벡터화 최적화를 실행할 수 있고, 거듭제곱 연산자(**)와 같은 수학적 기본 표현을 직관적으로 제공한다.
범용 언어 파이썬(Python)과 비교했을 때, 파이썬은 간결한 문법과 광활한 라이브러리 생태계를 보유하여 범용 데이터 과학과 인공지능 엔지니어링의 표준 인터페이스로 자리 잡았다. 하지만 파이썬은 실행 시점에 코드를 해석하는 인터프리터 언어이므로, 사전 정적 컴파일을 완료하여 CPU 및 분산 하드웨어의 파이프라인 성능을 100% 가동하는 Fortran의 초고속 부동소수점 처리 속도를 따라잡기 어렵다.
초고성능 연산 언어 Fortran은 복잡한 미적분 방정식과 대규모 다차원 배열 처리에 최적화된 내부 구조를 지닌다. 수학 공식을 논문이나 교과서에 쓰인 형태 그대로 직관적으로 코딩할 수 있도록 구문이 설계되어 있으며, 분산 메모리 환경의 메인프레임 및 초거대 슈퍼컴퓨터 인프라에서 수천 개 이상의 코어가 동시 협업하는 병렬 연산(HPC) 시 압도적인 연산 속도와 안정성을 보장하는 과학 계산의 변하지 않는 표준 도구다.
수치 계산에 특화된 Fortran도 모든 컴퓨팅 분야에서 완벽한 것은 아니다. 언어 설계의 목적이 오직 물리 방정식 연산에 집중되어 있어, 파일의 복잡한 텍스트 파싱(parsing)이나 데이터 그래픽 시각화(GUI 및 플롯팅) 영역은 상대적으로 매우 취약하다.
천문학적인 차분의 격자 물리 연산과 행렬 대수 처리는 Fortran 엔진이 가장 안전하고 빠르게 전담하여 연산한 뒤, 신속하게 표준 csv(Comman Separated Values) 텍스트 파일로 메모리를 배출한다. 이어서 데이터 제어 및 표현력이 풍부한 파이썬이 그 csv 파일들을 읽어들여 Matplotlib 라이브러리를 통해시각화 맵과 포물선 궤적, 기온 평활화 그래프 등으로 그려낸다.
이러한 수치 연산과 시각화의 명확한 역할 분담 체계가 현대 전산 과학 연구를 관통하는 흐름이다.
[!note] Fortran이 고성능 과학 계산에서 유독 빠른 이유
흔히 프로그래밍 언어를 배울 때 "Fortran은 행렬을 세로 방향(column-major)으로 저장하기 때문에 빠르다"라는 말을 듣곤 한다. 하지만 이는 데이터 배열 방식일 뿐, Fortran이 고성능 과학 계산에서 유독 압도적인 속도를 내는 진짜 비결은 따로 있다.
1. 포인터 에일리어싱(Pointer Aliasing)의 부재 C 언어에는 포인터 에일리어싱(Pointer Aliasing)이라는 강력하지만 위험한 기능이 존재한다. 쉽게 말해, '두 개 이상의 포인터(메모리 주소 가리키는 화살표)가 동시에 메모리의 똑같은 공간을 가리킬 수 있는 기능'이다. 이로 인해 C 언어 컴파일러는 CPU의 초고속 병렬 연산 기능을 쓰고 싶어도 "혹시 데이터가 꼬이면 어쩌지?"라는 잠재적 위험에 보수적으로 작동한다. 반면, Fortran은 철저하게 "서로 다른 배열은 절대 같은 메모리 공간을 공유하지 않는다"라는 엄격한 규칙을 따르기 때문에, 컴파일러 입장에서는 데이터가 꼬일 걱정이 없다. 덕분에 컴파일러는 아무런 제약 없이 루프를 쪼개고, 뒤섞고, 여러 CPU 연산을 분배하는 극단적인 병렬 최적화를 과감하게 수행할 수 있다.
2. 다차원 배열 연산의 태생적 최적화 Fortran은 태생부터 대규모 다차원 행렬 연산을 위해 설계된 언어다. 수십 년간 기상 예측, 천체 물리학, 양자역학 시뮬레이션 등 지구상에서 가장 거대한 계산을 처리하며 진화해 왔다. 그 결과, Fortran 컴파일러와 수치 해석 라이브러리(BLAS, LAPACK 등)는 대형 행렬을 만났을 때 이를 CPU가 가장 효율적으로 요리할 수 있도록 기저부터 튜닝되어 있다.
3. 메모리 할당의 단순함: 파편화 없는 덩어리 C 언어에서 다차원 배열이나 포인터 배열을 구현하다 보면, 메모리가 물리적으로 사방에 흩어지는 '파편화(fragmentation)' 현상이 발생하기 쉽다. 이 경우, CPU가 데이터를 가져올 때마다 메모리 공간을 찾아 헤매느라 속도가 느려진다. 반면 Fortran은 대규모 다차원 배열을 메모리에 하나의 거대하고 단순한 연속된 덩어리 구조로 할당한다. CPU 입장에서는 일렬로 늘어선 데이터를 막힘없이 한 번에 쓸어 담을 수 있어 관리 비용과 지연 시간이 극적으로 줄어든다.
Fortran은 과거의 유산으로 잊히지 않고 최근 다시 주목받고 있다. 글로벌 프로그래밍 언어 인기 지표인 TIOBE 인덱스에 따르면, Fortran은 2021년 상위 20위 권에 복귀한 후 2024년에는 한때 10위권 내에 진입했으며 현재도 상위권을 유지하고 있다. 1950년대에 등장한 언어가 수십 년 만에 순위를 역주행하는 이례적인 현상이 벌어진 것이다.
이러한 반등의 배경에는 AI 시대와 고성능 컴퓨팅(HPC) 수요의 폭발이 있다. 인공지능 모델의 학습과 추론, 그리고 복잡한 과학적 시뮬레이션에는 막대한 수학적 연산이 필수적이다. Fortran은 다른 주류 언어들과 달리 수치 계산과 다차원 배열 연산을 언어 수준에서 직관적으로 지원하여 고성능 하드웨어(GPU 및 HPC 클러스터)에서 성능을 극한으로 끌어올린다.
또한 전통적인 시스템의 유지보수와 확장 수요도 한몫한다. 기상 예측, 항공우주 시뮬레이션, 원자력 시스템 등 전 세계의 수많은 핵심 인프라 소프트웨어가 이미 Fortran으로 구축되어 있다. 최근 AI 시스템이 이러한 시뮬레이션 소프트웨어와 융합되면서, 수십 년간 검증된 견고한 자산을 최신 환경에 맞춰 확장하려는 움직임이 가속화되었다. Fortran이 막대한 연산 능력이 요구되는 특수 목적 분야에서 대체 불가능한 가치를 재증명하며 화려하게 귀환했다고 볼 수 있다.
[!important] 1.4 개발 환경: Colab과 gfortran
이 책에서는 로컬 컴퓨터에 컴파일러를 설치하는 복잡한 과정과 예제 마다 파일이 쌓이는 비용을 줄이고자 클라우드 기반의 구글 코랩 (Google Colab)과 gfortran (GNU Fortran) 조합의 개발 환경을 기반으로 하여, Fortran 문법을 배우는데만 집중할 수 있도록 구성 하였다.
구글 코랩은 구글이 무료로 제공하는 브라우저에서 실행되는 주피터 노트북 (Jupyter Notebook) 환경이다. 클릭 한 번이면 클라우드의 리눅스 우분투(Ubuntu) 기반의 가상 머신이 할당되고, 그 위에서 코드를 실행할 수 있다.
코랩의 빠른 실행을 위한 순서는 다음과 같다.
코랩.research.google.com에 접속해 구글 계정으로 로그인한다.파일 → 새 노트(New notebook)로 빈 노트북을 만든다.Shift+Enter(또는 셀 왼쪽의 ▶ 실행 버튼)를 누르면 실행되고, 결과가 셀 바로 아래에 나타난다.%%writefile 파일명을 붙이면 그 셀의 내용이 해당 파일로 저장된다.!apt-get install -y gfortran 처럼 코드 줄 맨 앞에 !를 붙이면 리눅스 쉘 명령이 실행된다.코랩에서 작동하는 Fortran 컴파일러는 gfortran이로서 GNU 컴파일러 모음(GCC)에 포함된 무료·오픈소스 Fortran 컴파일러이다. 인텔 ifx 등의 상업용 컴파일러도 있지만, 입문 단계에서는 어디서나 똑같이 동작하고 무료인 gfortran이 가장 무난하다. 코랩 상에서 사용하면, 로컬 컴퓨터 상의 개발 환경을 구축하는데 많은 시간과 비용을 소비하지 않아도 되는 최적의 학습을 위한 개발 환경이 구현된다.
컴파일러 gfortran은 코랩에 기본 설치되어 있지 않으므로 코랩 셀에서 직접 설치한다. 코랩에서는 리눅스 쉘 명령이므로 줄 맨 앞에 !를 붙여 아래와 같이 실행한다.
!apt-get install -y gfortran
설치가 끝나면 버전을 확인한다. 이때도 코랩에서는 맨 앞에 !를 붙인다.
!gfortran --version
다음과 같은 출력이 나오면 성공이다. gfortran의 버전 숫자는 다를 수 있다.
GNU Fortran (Ubuntu 13.3.0-...) 13.3.0
...
코랩 런타임을 새로 시작하거나 일정 시간 방치해 연결이 끊기면, 설치했던 gfortran이 사라진다. 그럴 때는 !apt-get install -y gfortran을 다시 한 번 실행해야 한다. 따라서, 매 세션 첫 셀에 설치 명령을 넣어 두는 것이 편하다.
파이썬과 같은 인터프리터 언어 코딩에 익숙한 사람에게는 약간 불편할 수 있지만, 별도의 개발 환경을 구축하지 않고 로컬 컴퓨터 상에서 Fortran을 코딩, 컴파일, 실행할 수 있는 것과 비교하면 상당히 편리한 개발 환경이 된다.
[!important] 1.5 첫 프로그램
이제 우리의 첫 프로그램을 만들어 보자. 구글 코랩 환경에서 Fortran을 다루는 핵심 요령은 노트북 셀을 기능에 따라 세 종류로 나누어 사용하는 것이다.
Fortran 코드 셀: 셀의 첫 줄에 %%writefile 파일명.f90을 붙인다. 이렇게 하면 그 아래 작성한 모든 내용이 텍스트 파일로 저장된다.
컴파일 및 실행 셀: 리눅스 서버에서 수행되는 셸 명령이므로, 각 줄의 맨 앞에 느낌표(!)를 붙여서 실행한다.
파이썬 시각화 셀: Fortran이 계산하여 내보낸 데이터 파일을 읽어와 그래프를 그리는 파이썬 코드가 작성되는 셀이다.
하나의 구체적인 예제를 끝까지 따라가며 이 세 가지 셀이 어떻게 맞물려 돌아가는지 실제 예제를 통해해 익혀 보자.
인사말을 출력하고, 섭씨 온도를 화씨 온도로 변환하는 표를 계산하여 temp.csv 파일로 저장하는 프로그램을 코랩의 세 개의 셀 상에서 구현한다.
코랩의 새 코드 셀을 열고 아래 내용을 그대로 입력한 뒤 실행(Ctrl + Enter)한다. 인사말을 출력한 뒤, 섭씨 $-40$도부터 $100$도까지 $10$도 간격으로 화씨로 변환해 저장하는 코드이다.
%%writefile hello.f90
program hello
implicit none
integer :: i, u
real :: celsius, fahrenheit
print *, "Hello, Fortran!"
open(newunit=u, file="temp.csv", status="replace", action="write")
write(u, '(A)') "celsius,fahrenheit"
do i = -40, 100, 10
celsius = real(i)
fahrenheit = celsius * 9.0 / 5.0 + 32.0
write(u, '(F8.2, ",", F8.2)') celsius, fahrenheit
end do
close(u)
print *, "temp.csv written"
end program hello
지금은 이 프로그램의 세부 문법보다 전체적인 구조만 익히면 충분하다. program으로 시작해 end program으로 끝나고, 둘째 줄에 implicit none이 있으며, 변수를 먼저 선언한 뒤 계산하고 출력한다는 큰 틀을 기억하자.
온도 변환의 핵심은 fahrenheit = celsius * 9.0 / 5.0 + 32.0 한 줄이다. 각 줄의 정확한 문법적 의미(프로그램 단위, 선언부와 실행부, 식별자 규칙, implicit none의 원리)는 2장에서 다루고, 여기에 등장하는 반복문 do와 파일 입출력(open/write/close)은 5장과 6장에서 다룬다. 지금은 Fortran이 값을 계산해 csv로 내보내는 과정으로만 익혀둔다.
작성한 코드를 기계어로 번역(컴파일)한 뒤 실행해 보자. 코랩 환경이므로 셸 명령을 내리기 위해 줄 맨 앞에 각각 느낌표(!)를 붙인다. 만약 개인 PC나 리눅스 서버 등 다른 일반 개발 환경에서 실습한다면 앞의 !를 제외하고 입력해야 한다.
!gfortran -O2 -std=f2018 -Wall hello.f90 -o hello
컴파일 명령의 각 옵션이 가지는 의미는 다음과 같다.
gfortran: GNU의 Fortran 컴파일러를 호출한다.
-O2: 최적화(optimization) 수준 2 단계를 적용한다. 연산 속도를 극대화하기 위해 컴파일러가 코드를 알아서 다듬도록 한다.
-std=f2018: Fortran 2018 표준 규격을 강제한다. 표준에 맞지 않는 레거시 확장을 실수로 사용하면 경고를 띄워준다.
-Wall: 컴파일러가 잡아낼 수 있는 모든 경고(Warning all)를 켠다. 이 책에 수록된 모든 예제는 이 옵션 아래에서 아무런 경고 없이 깨끗하게 컴파일되도록 정돈되어 있다.
hello.f90: 컴파일할 대상이 되는 Fortran 소스코드 파일이다.
-o hello: 빌드되어 나올 실행 파일의 이름을 hello로 지정한다. 이 옵션을 생략하면 기본 파일명으로 a.out이라는 파일이 생성된다.
컴파일 후 생성된 hello라는 프로그램을 아래와 같이 실행해 보자.
!./hello
./hello 명령은 방금 컴파일로 만들어진 실행 파일을 실행하라는 의미다(./는 '현재 폴더'라는 의미). 성공적으로 실행되면 화면에 다음과 같이 출력되며, 동시에 같은 디렉터리에 temp.csv 파일이 만들어진다.
Hello, Fortran!
temp.csv written
생성된 temp.csv의 앞부분 내용이 궁금하다면 코랩 셀에 !head temp.csv를 입력하여 확인할 수 있다. 데이터는 아래와 같은 형태로 정렬되어 저장된다.
celsius,fahrenheit
-40.00, -40.00
-30.00, -22.00
-20.00, -4.00
-10.00, 14.00
우리가 만든 Fortran 프로그램이 수치 계산을 정확하게 수행하여 데이터 파일로 뽑아냈음을 확인할 수 있다.
드디어 첫 Fortran 프로그램을 완성하였다.
[!warning] [흔한 실수] 컴파일과 실행은 따로 구분된다.
파이썬은 코드를 셀에 기입하면 바로 실행되지만, Fortran은 컴파일(번역) → 실행의 두 단계를 더 거친다. 코드를 수정했다면 반드시 다시 컴파일해야 변경이 반영된다. 수정했는데 결과가 그대로라면 십중팔구 재컴파일을 빠뜨린 것이다.
파이썬 등 인터프리터 언어에 익숙한 독자는 컴파일 과정이나 코랩 상에서
!을 사용하는 추가적인 요구되는 행위에 상당한 번거로움을 느낄 것이다. 하지만, Fortran의 압도적인 계산 속도를 감안한다면 감내할 만한 불편함이라 생각하자.
이제 Fortran이 계산하여 만들어낸 temp.csv 파일을 파이썬으로 읽어와 그래프로 그려 보자. 코랩의 세 번째 코드 셀을 열고 아래 파이썬 코드를 작성한 뒤 실행(Ctrl + Enter)한다. 간단한 파이썬 matplotlib문법은 부록 B를 참조할 수 있다.
import csv
import matplotlib.pyplot as plt
c, f = [], []
with open("temp.csv") as file:
reader = csv.reader(file)
next(reader) # skip the header row
for row in reader:
c.append(float(row[0]))
f.append(float(row[1]))
plt.figure(figsize=(8, 4.5))
plt.plot(c, f, marker="o", color="#c0392b")
plt.axhline(0, color="gray", linewidth=0.8)
plt.axvline(0, color="gray", linewidth=0.8)
plt.xlabel("Celsius")
plt.ylabel("Fahrenheit")
plt.title("C to F: computed in Fortran, plotted in Python")
plt.grid(True, alpha=0.3)
plt.savefig("temp.png", dpi=120)
plt.show()
파이썬의 csv.reader를 활용해 저장된 데이터 파일을 한 줄씩 읽어 들인다. 첫 번째 칸의 섭씨 값은 리스트 c에, 두 번째 칸의 화씨 값은 리스트 f에 차곡차곡 모은다. 그다음 plt.plot을 통해 두 값을 연결하는 선그래프를 완성한다.
코드를 실행하면, 우상향 직선 그래프가 화면에 나타난다. 화씨 온도가 섭씨 온도에 정확히 정비례하여 상승하는 변환 관계가 시각적으로 한눈에 들어온다.
직선이 가로축과 세로축을 지나는 교차점을 살펴보면, 섭씨 $0$도일 때 화씨 $32$도를 가리키는 점, 그리고 앞서 텍스트로 확인했던 섭씨 $-40$도와 화씨 $-40$도가 정확하게 교차하는 지점도 직관적으로 파악할 수 있다.
![[Pasted image 20260716141507.png]]
[!note] 컴파일러 최적화(Compiler Optimization)
컴파일러가 소스 코드(source code)를 기계어로 번역할 때, 프로그램의 기능(결과)은 유지하면서도 더 빠르고 효율적으로 작동하도록 코드를 자동으로 다듬는 과정을 최적화라고 한다. 최적화의 대표적인 기법들은 아래와 같다.
1. 불필요한 코드 제거 (Dead Code Elimination)
실행 흐름상 실행될 가능성이 없는 코드나 사용되지 않는 변수와 연산을 찾아내어 삭제한다. 프로그램의 크기를 줄이고 불필요한 메모리 낭비를 막아준다.
2. 상수 접기 및 전파 (Constant Folding & Propagation)
예를 들어,
int x = 3 + 5;처럼 미리 계산할 수 있는 수식이 있으면, 실행 이전 컴파일 단계에서int x = 8;로 수정한다(상수 접기). 또한 이x가 다른 곳에서도 변하지 않고 사용되면8을 대입하여 연산 과정을 줄인다(상수 전파).3. 루프 최적화 (Loop Optimization)
반복문은 프로그램 실행 시간의 대부분을 차지하기 때문에 최적화가 가장 중요한 부분이다.
루프 불변 코드 이동(Loop-Invariant Code Motion): 반복문 안에서 매번 계산되지만 항상 결과가 같은 연산이 있다면, 이를 반복문 밖으로 빼내어 딱 한 번만 계산하게 만든다.
루프 전개(Loop Unrolling): 반복 횟수가 적은 반복문은 반복 제어 오버헤드를 줄이기 위해 루프 내부 코드를 연속으로 나열하는 방식으로 바꾼다. 여기서, 오버헤드(overhead)는 본래 목적한 작업 외에 부가적으로 들어가는 시간, 메모리 소모 등의 간접 비용을 뜻한다.
4. 인라인 확장 (Inline Expansion)
크기가 작은 함수를 호출할 때, 함수를 찾아갔다 돌아오는 과정에서 발생하는 오버헤드를 줄이기 위해 함수 호출 부분에 함수의 실제 코드를 그대로 삽입하는 기법이다.
[!important] 오류 학습
프로그래밍에서 오류는 피할 수 없다. 중요한 것은 오류 메시지를 읽어 원인을 파악하는 습관과 오류를 수정하는 디버깅(debugging) 능력이다. Fortran에서 마주치는 오류는 크게 네 가지로 나뉜다.
번역(컴파일) 단계에서 문법이 어긋나 멈추는 오류다. 가장 흔하지만, 어디가 틀렸는지 알려 주므로 그나마 다루기 쉽다. 예를 들어 소수점 자리에 마침표 대신 콤마(,)를 찍으면 아래와 같은 에러가 나타난다.
%%writefile e_compile.f90
program e_compile
implicit none
real :: g
g = 9,8 ! decimal point must be a dot, not a comma
print *, g
end program e_compile
컴파일:
!gfortran -O2 -std=f2018 -Wall e_compile.f90 -o e_compile
컴파일 결과:
e_compile.f90:4:4:
4 | g = 9,8
| 1
Error: Unclassifiable statement at (1)
implicit none을 기입한 덕분에 잡히는 컴파일 에러도 있다. 선언하지 않은 변수를 쓰면 발생한다. 예를 들어, length를 lenght로 잘못 적으면 Error: Symbol 'lenght' at (1) has no IMPLICIT type; did you mean 'length'?과 같이 오타까지 찾아 준다.
컴파일은 통과했지만 실행 중에 멈추는 오류가 실행 오류(runtime error)이다. 예를 들어, 존재하지 않는 파일을 읽으려 하면 컴파일은 되지만 실행 단계에서 멈춘다.
컴파일도 실행도 멀쩡한데 답이 틀린 오류다. 오류 메시지가 전혀 없어 가장 위험하다. 대표적인 예가 1.8절에서 본 변환식에 정수 나눗셈을 하는 경우이다.
%%writefile e_logic.f90
program e_logic
implicit none
integer :: nine = 9, five = 5
real :: celsius, fahrenheit
celsius = 100.0
fahrenheit = celsius * (nine / five) + 32.0 ! nine/five = 1, not 1.8
print *, "fahrenheit:", fahrenheit
end program e_logic
컴파일:
!gfortran -O2 -std=f2018 -Wall e_logic.f90 -o e_logic
실행:
!./e_logic
실행 결과:
fahrenheit: 132.000000
100°C는 212°F여야 하는데 132이 나왔다. Fortran에서 정수 끼리 나눗셈 nine / five은 9/5 = 1.8이 아니라 1이다. 한쪽을 실수로 바꾸면(real(nine) / five) 212.000000 으로 올바로 출력된다. 이처럼 정수 나눗셈은 경고도 없이 틀린 답을 만들 수 있어, 수치 코드에서 가장 흔한 버그 원인 중 하나이다(4장).
실수를 2진수로 저장하면서 생기는 미세한 오차가 쌓여 생기는 오류다. 예를 들어 0.1을 열 번 더해 보자.
%%writefile e_round.f90
program e_round
implicit none
integer :: i
real :: total
total = 0.0
do i = 1, 10
total = total + 0.1
end do
print *, "total :", total
print *, "total == 1.0 :", (total == 1.0)
end program e_round
컴파일:
!gfortran -O2 -std=f2018 e_round.f90 -o e_round
실행:
!./e_round
실행 결과:
total : 1.00000012
total == 1.0 : F
정확히 1.0이 아니라 1.00000012가 나온다. 이 결과가 1.0과 같은지 묻는 비교 결과는 거짓(F)이 출력된다. 0.1이 2진수로 정확히 표현되지 않아 생긴 작은 오차가 누적된 결과이다. 여기서 얻을 교훈은 실수를 비교 연산자 ==로 정확히 비교하면 안 된다는 것이다(3장).
논리 오류는 컴파일이나 실행 단계에서는 드러나지 않지만, 개발자가 의도한 논리 자체 또는 이를 구현한 코드의 흐름이 잘못된 경우를 말한다. 컴파일러가 잡아낼 수 없는 영역이므로 개발자의 꼼꼼한 논리적 사고와 구현 능력이 필요하다. 여기서 예시는 생략한다.
[!note] 오류는 빨리 만날수록 좋다
위 오류들은 컴파일 → 런타임 → 로직 → 반올림 순으로 점점 "조용해서" 더 위험하다. 컴파일 에러는 즉시 잡히지만, 로직·반올림 에러는 멀쩡히 실행되며 틀린 답을 낸다. 코드 내
implicit none과 컴파일 옵션-Wall은 문제들을 명시적으로 드러내어, 더 위험한 실행 단계에서의 잠재적 오류를 줄여 준다.[!warning] [흔한 실수] matplotlib에서 한글 깨짐
코랩의 기본 폰트에는 한글이 없어, 그래프 제목·축 라벨에 한글을 쓰면 네모(□)로 깨지고 경고가 뜰 수 있다. 이 책의 시각화 코드가 라벨을 영문으로 적는 이유다. 한글 라벨이 꼭 필요하면 시각화 셀 맨 위에서 한글 폰트를 설치·지정한다.
bash !apt-get install -y fonts-nanumpython import matplotlib.font_manager as fm import matplotlib.pyplot as plt fm.fontManager.addfont("/usr/share/fonts/truetype/nanum/NanumGothic.ttf") plt.rcParams["font.family"] = "NanumGothic" plt.rcParams["axes.unicode_minus"] = False
[!important] 요약
!apt-get install -y gfortran이며, 런타임이 초기화되면 다시 설치한다.gfortran -O2 -std=f2018 -Wall 파일.f90 -o 실행파일. 코드를 수정하면 반드시 재컴파일한다.implicit none과 -Wall이 조용한 오류를 줄여 준다.%%writefile/! 누락, 정수 나눗셈(로직 에러), matplotlib 한글 폰트.[!important] 연습 문제
apt-get으로 gfortran을 설치하고 gfortran --version으로 버전을 확인하라. 출력된 주 버전 번호를 적어라.hello.f90을 그대로 따라 작성·컴파일·실행하라. 화면 출력 두 줄과 temp.csv의 첫 두 줄(헤더 포함)을 적어라.hello.f90의 변환 범위 do i = -40, 100, 10을 0, 200, 10으로 바꾼 뒤, 다시 컴파일하지 않고 실행하면 temp.csv의 내용이 달라지는가? 그 다음 재컴파일하면 어떻게 달라지는가?myname.f90을 작성하라.r = 2.0에 대해 넓이(pi * r**2)를 출력하는 프로그램을 작성하라. pi는 parameter로 선언하라.컴파일 명령에서 -o hello를 빼면 어떤 실행 파일이 생기는가? 그 파일을 실행해 보라.
직사각형의 가로·세로를 변수로 선언·초기화하고 넓이와 둘레를 함께 출력하는 프로그램을 작성하라.
pi를 parameter로 선언한 뒤, 프로그램 안에서 pi = 3.0처럼 값을 다시 대입해 보라. 어떤 컴파일러 메시지가 나오는가? 왜 그런지 설명하라.hello.f90을 거꾸로 바꿔, 화씨 32도부터 212도까지 20도 간격으로 섭씨로 변환(c = (f - 32.0) * 5.0 / 9.0)하는 표를 csv로 출력하고, 가로축을 화씨·세로축을 섭씨로 그려라. 두 눈금이 같아지는 −40을 그래프에서 찾아보라.hello.f90을 참고하되, 출력값을 i*i로 바꿔 보라.)