[정보 처리] 17. 데이터 전환(ETL), 프로그래밍 이론

2025. 5. 17. 11:07·Study/Computer Science

- 이번 포스팅에선 기존에 데이터베이스에 존재하는 중요하고 막대한 데이터를 이관하거나 변환 할 때에 어떤식으로 처리를 해야하는지에 대한 지식을 정리할 것입니다.

 

- 또한 정보처리의 범위내에서 이론적인 프로그래밍 개념에 대해 설명드리겠습니다.

 

미리 말씀드리지만, 정보처리 시리즈에서는 실용적 프로그래밍 개발 방법이나 코딩 방식에 대해서 다루지 않을 것입니다.

프로그래밍 범위는 따로 두꺼운 책이 있을 정도로 범위가 넓으며, 실용에 해당하는 부분이므로 정식으로 배우시거나 독학하시는 것을 추천드리기 때문입니다.(추후 이 부분을 제 블로그에서 정리할 가능성도 있지만, 책, 유튜브, 다른 분들의 블로그에 이미 굉장히 잘 정리된 자료를 쉽게 찾으실 수 있을 것입니다.)

 

본 게시글에서 다루는 것은, 이론만 다루는 분들을 상정한 개념 정리와, 더 깊이 진행하시려면 어떤 주제를 어떤 방식으로 진행하시는 것이 좋을지에 대해 소개를 드릴 것입니다.

 


[데이터 전환]
(ETL)
- ETL 이란, 데이터 이관(Data Migration)을 위해 필요한 작업인 추출(Extraction), 변환(Transformation), 적재(Loading) 의 각 영어 앞 글자를 따온 표현입니다.

기존의 원천 시스템에서 데이터를 추출하여 목적 시스템이 필요로 하는 형식으로 변환한 후 적재하는 것 까지의 일련의 과정을 의미하며, 실무상에서는 현재 사용중인 중요한 운영 데이터를 런타임에 가져오는 경우도 많을 것이므로 주의를 기울여야 할 것입니다.

 

- 데이터 전환 작업은 표준화, 안전성, 우선순위를 고려해야 합니다.

데이터 전환 파이프라인(수집, 분석, 입력 프로세스), 업무 표준 데이터(관리 항목, 관리 코드 등)를 표준화하고,

데이터 전환 및 결과 검증을 책임질 전담 인원을 구성하여 작업 효율과 안정성을 확보하며,

개발 일정, 업무 중요도, 데이터 유형 등을 고려한 우선 순위 및 데이터 전환 범위 및 시스템 간 전환 절차를 고려하여 전환 시나리오를 구성해야 합니다.

 

- 데이터 전환 작업은 시스템 개발 프로젝트와 유사한 개념이므로,

요구사항 분석 -> 설계 -> 구현 -> 테스트 의 절차로 수행됩니다.


(데이터 전환 프로세스)
- 앞서 설명한 데이터 전환 작업의 절차를 단계별로 나열하여 설명하겠습니다.

1. 데이터 전환 계획 및 요건 정의

데이터 전환을 위해 현행 정보 시스템과 원천 데이터의 구조를 분석하는 단계입니다.

 

작업 사항은 아래와 같습니다.

_프로젝트 범위, 목표, 인원, 계획, 관리 내용 설정

_전환 작업에 필요한 컴퓨터 환경 파악

_장비, 솔루션, 네트워크 구성, 데이터 형식 및 크기 파악

_ 목표 시스템 분석 및 데이터베이스 구조 분석

_이해관계자의 데이터 전환 요건 정의

 

2. 데이터 전환 설계

정의된 전환 요건과 업무 흐름을 기준으로 데이터 전환을 위한 데이터 매핑 및 검증 규칙을 설계하는 단계입니다.

 

작업 사항은 아래와 같습니다.

_데이터의 논리적 매핑(어느 데이터가 어떻게 표현될지) 설계

_코드상 데이터가 어떻게 매핑될지 설계

_변환된 데이터가 잘 전환되었는지 어떻게 파악할 수 있는지에 대한 검증 규칙 설계

_데이터 매핑 관련 내용 기술

 

3. 데이터 전환 개발

전환 설계서를 기반으로 전환 프로그램을 구현하는 단계입니다.

 

작업 사항은 아래와 같습니다.

_개발 표준 및 방법론을 준수하여 개발 환경을 구축합니다.

_데이터 전환 설계서에 따라 전환 프로그램을 구현합니다.

_정의된 검증 규칙과 기준에 부합하는 전환 결과 검증용 프로그램도 구현합니다.

 

4. 데이터 전환 테스트 및 검증

데이터 전환 테스트를 진행하고 진행 결과를 검증하는 단계입니다.

실제 데이터 전환 작업은 무척이나 민감하고 중요한 작업이 될 수도 있기에 사전 기능 테스트를 통해 충분한 검증을 거칠 필요가 있습니다.

 

작업 사항은 아래와 같습니다.

_데이터 추출, 데이터 변환, 데이터 적재 각각에 대한 결과 검증

_검증 과정에서 발견된 수정사항 반영

_전환 테스트를 N번 반복하여 데이터 정합성 확보

 

5. 데이터 전환

실제 원천 시스템의 데이터를 추출하고, 변환하고, 적재하는 단계입니다.

 

작업 사항은 아래와 같습니다.

_데이터 이관 진행 후 결과 검증

_전환 데이터 모니터링

_모니터링 단계에서 발견된 문제점 파악 후 후속 단계 진행

_전환 완료 보고서 작성 후 의사 결정자에게 보고

(데이터 전환 프로그램의 종류)
1. SQL 스크립트 직접 변환

SQL 스크립트를 작성하여, 소스 DB에서 목적 DB로 직접 변환하는 방법입니다.

 

2. 프로그램 직접 변환

응용 프로그램을 이용하여 소스 DB에서 목적 DB로 직접 변환하는 방법입니다.

 

3. 프로그램 경유 변환

응용 프로그램을 이용하여 소스 DB를 중간 형태의 파일로 변환하고 다시 목적 DB로 변환하는 방법입니다.

프로그램 직접 변환 방법은 전환 프로그램이 소스에서 목적지로 바로 전송되는 것이라면,

경유 변환 방법은 일단 CSV, JSON 등의 중간 파일 등을 경유하여 전송한다는 차이가 있습니다.(중간 파일을 DB 에 넣기 위해 DBMS 의 import 를 이용할 수 있습니다.)

 

[데이터 정합성 검증]
(데이터 정합성 검증 방안)
- 데이터 정합성의 검증 기준은 아래와 같습니다.

1. 참조 무결성

데이터 품질 향상을 위해 테이블 간 컬럼과 로우, 데이터의 정합성 등을 확인하는 것을 권장합니다.

 

2. 산술적 정당성

전환된 데이터가 목표 시스템의 모델을 준수하는 지를 확인합니다.

전체 및 부분 간의 합계 일치 여부, 전체 및 부분 간의 건수가 일치 하는지 여부, 범위 값이 일치하는지 여부를 확인합니다.

 

3. 물리적 정당성

매핑된 모든 데이터가 목표 시스템 내에서 적절히 전환되었는지 확인합니다.

데이터 형식이 일치하는지, Null 값 제약이 준수되었는지 등을 확인하고,

통계 비교, 업무 규칙 적용, 정제 규칙을 적용한 데이터 일치성을 확입합니다.

 

4. 물리 객체

적재 이후 Table 별 인덱스 수 및 내용이 일치하며 정상 상태인지를 확인합니다.

 

(데이터 전환 결과 보고서)
- 데이터 전환 작업을 마친 후에는 결과 분석 및 보고서를 작성하여 관계자와 정보를 공유해야 합니다.

아래와 같은 절차로 진행됩니다.

1. 데이터 전환 결과 분석

데이터 전환 결과로 얻은 데이터에서 데이터 전환 내용, 시간, 오류 내역, 오류율을 점검하여 원인을 분석합니다.

특히 오류 데이터 값을 분석하여 다음 단계인 데이터 정제 요청이 필요한지 판단할 수 있는 근거를 제시해야 합니다.

 

2. 결과 보고서 작성

데이터 전환 결과 분석 상세 내용을 문서화합니다.

결과 보고서에는 전환 일정, 내역, 작업 인원, 전환 장소 및 결과 등이 포함되어야 합니다.


(데이터 관리 책임과 역할)
- 데이터 관리 관련 마지막 내용으로,

기업 내 데이터 관리 업무와 연관된 연관자의 종류와 역할에 대해 정리하겠습니다.

 

1. CIO(Chief Information Officer) / EDA(Enterprise Data Administrator)

개괄적 관점에서 데이터 관리를 총괄하며 관리 정책을 수립하는 역할을 하는 직책입니다.

데이터 관리자 간 이슈사항을 조정합니다.

 

2. DA(Data Administrator)

개념적 관점에서 전사 데이터 변경 관리를 총괄하며, 전사 데이터 통합 모델을 관리하는 직책입니다.

데이터 표준을 개발 및 조정하는 역할입니다.

 

3. Modeler

논리적 관점에서 특정 기능 영역에 대한 요구사항 및 이슈사항을 조정하고 통합하는 직책입니다.

특정 기능 영역의 비즈니스 룰을 토대로 데이터 모델링을 수행합니다.

 

4. DBA(Database Administrator)

물리적 관점에서 데이터베이스를 디자인하고 형상관리를 하며, 시스템을 다루는 직책입니다.

데이터베이스 모니터링과 튜닝, 보안 설정을 담당합니다.

 

5. User

운용적 관점에서 데이터베이스를 활용하며 데이터에 대한 추가 요건을 요청하는 직책입니다.

 

[프로그래밍 개론]

- 프로그래밍이란, 인간을 대신하여 작업하는 주체인 컴퓨터가 특정 목적에 따라 움직이게 만드는 작업입니다.

코딩이란, 프로그래밍 개념 안에 속하며, 컴퓨터가 이해하는 언어로 컴퓨터를 어떻게 움직이는지에 대한 명령 문서(코드)를 작성하는 작업입니다.

 

-프로그래밍 언어란,

코딩을 하는 언어라고 생각하시면 됩니다.

컴퓨터에게 명령을 내릴 때는, 사람이 사람에게 말을 하듯 하는 것이 아닙니다.

가장 근본적으로는 1과 0으로 이루어진 Binary 데이터로 컴퓨터 회로를 움직이는 개념인데, 이를 사람이 사용하기 쉬운 형태로 만든 규칙이 프로그래밍 언어입니다.

 

자세한 컴퓨터 구동 원리에 대해서는 여기선 설명하지는 않겠지만(자세히 알아보고 싶으신 분이라면 컴퓨터 공학의 컴퓨터 구조 과목을 찾아보세요.),

컴퓨터가 이해할 수 있는 유일한 형태의 데이터가 존재할 때, 이를 인간 개발자가 직접 생각해서 작성하기에는 너무나 쓸데없는 노력이 많이 소비될 것이기에, 마치 사람에게 명령하듯(영어로 명령하듯) 명령문을 작성하고,

명령문을 해석해서 컴퓨터의 언어로 해석하는 기능을 수행하는 또다른 프로그램(컴파일러라고 합니다.)으로 프로그래밍 언어의 명령문을 해석하여 컴퓨터 언어로 번역하고, 이 컴퓨터 언어로 컴퓨터를 조작하는 것이 프로그래밍 언어의 의의입니다.

 

개발자라는 직업의 첫걸음은 프로그래밍 언어의 사용법을 익히는 것이고,

개발자의 성장은 프로그래밍 언어에 숙달하는 것이라고 할 수 있을만큼 개발자 업무의 가장 큰 영역을 차지합니다.

 

- 참고로 프로그래밍 과목은 컴퓨터 관련 학과를 나오신 분들이 개발자라는 직업에 대한 진출을 꺼리는 가장 큰 이유이기도 합니다.

재밌다고 생각하시는 분이라면 다른 어떤 일보다 재밌는 일일 테지만, 그렇지 않으신 분이라면 생각하기도 싫으실 만큼 호불호가 심한 영역인데,

다행인지 아닌지 최근에는 생성형 AI 가 프로그래밍을 어느정도 대신해주고 있으며,

나중에는 아예 프로그래밍 분야가 개발자의 손을 떠날 가능성도 있습니다.

 

현 시점에서 개발자가 본인이 코딩을 직접 전부 하는 것이 아니라 AI 에이전트를 사용해서 자연어 기반으로 만들어진 코드를 그대로 사용하거나 수정하여 사용하는 방식으로 개발하는 방법론이 대두되고 있으며,

이를 바이브 코딩(Vibe Codind)이라고 부릅니다.

 

개인적으로는 프로그래밍을 좋아하는 편이라 안타까운 상황이라 생각하고 있지만,

어찌되었건 경쟁력을 기르기 위해서 앞으로는 AI 활용을 잘 하는 것이 중요할 것입니다.

 

- 기업 단위에서 프로그래밍을 진행할 때,

우리 기업이 어떤 개발 언어를 사용할지에서부터 생각해야 한다면, 해결하고자 하는 작업에 적합한지, 현재 구인할 수 있는 개발자 풀 내의 숙련자가 얼마나 되는지, 소프트웨어가 수행될 환경이 무엇이고 어떤 평가 지표를 우선시하는지 등을 고려해야합니다.

 

일반적으로는 아래와 같은 선정 기준이 있습니다.

1. 적정성 : 목표하는 개발 시스템의 목적에 부합

2. 효율성 : 적은 시간과 노력으로 원하는 목표에 도달

3. 이식성 : 일반적인 운영 환경에 설치

4. 친밀성 : 개발자의 언어에 대한 이해도

5. 범용성 : 다양한 경험 사례와 사용 분야

6. 유지보수성 : 개발 당시뿐 아닌 미래의 유지보수 고려

 

- 프로그래밍 언어는 저급 언어와 고급 언어로 나뉩니다.

저급 언어는 컴퓨터에 가까운 언어입니다.(예를들면 어셈블리어)

앞서 말했듯, 컴퓨터는 1과 0의 조합으로 동작하며, 이에 가깝게 모든 것을 조작하고 배치하여 코딩한다면 성능은 높아지겠지만 개발자가 이해하기 어렵고 작성하기도 어렵기에 생산성이 낮아질 것이고,

반면 인간이 이해하기 쉬운 자연어와 비슷한 고급 언어는,(예를들면 C, Java, Python)

컴퓨터의 관점에서 작성되지 않기에 해석 과정에서 비합리적이고 비 효율적인 컴퓨터 언어가 만들어져서 성능은 낮아지겠지만, 개발자가 이해하기 쉽고 작성도 쉬워서 생산성이 높아진다는 장점이 있습니다.

 

이제까지의 개발 환경의 발전은 저급 언어에서 시작하여 고급 언어를 선호하였으며, 기술의 발전으로 고급 언어임에도 성능이 높아지도록 발전해왔습니다.

 

- 프로그래밍 언어 번역기

작성한 코드를 논리적으로 분석하여 컴퓨터를 동작시키기 위한 목적으로 만들어진 프로그램을 프로그래밍 언어 번역기라고 분류할 수 있습니다.

1. 어셈블러 : 저급 언어인 어셈블리어를 기계어로 번역해주는 프로그램입니다.

2. 컴파일러 : 고급 언어를 기계어 또는 중간언어(어셈블리어, JVM 바이트 코드 등)로 번역해주는 프로그램입니다. 고급언어 컴파일 결과로 어셈블리어가 나온다면 그 결과물을 어셈블러로 또 번역하여 최종적으로 컴퓨터를 조작합니다.

3. 인터프리터 : 고급 언어 코드 중 인터프리터 언어를 그대로 해석해서 바로 동작에 적용하는 프로그램입니다.

위의 두 번역기와는 개념이 다른게, 위 번역기들은 번역 결과물을 생성하지만, 인터프리터는 그냥 코드를 그대로 번역하여 바로 컴퓨터 동작으로 실행시킵니다.(예를들어 텍스트 파일 내용에 A 를 입력한 횟수대로 print("Put A") 라고, 모니터에 출력하도록 프로그램을 만들었다면 그 프로그램이 해당 규칙을 가진 인터프리터를 만든 것입니다.)

 

(프로그래밍 언어의 종류)

- 일반적인 프로그래밍 언어의 종류는 아래와 같은 종류가 있습니다.

 

1. C

1972 년 벨 연구소 소속 데니스 리치에 의해 만들어진 프로그래밍 언어입니다.

UNIX 운영체제를 만들기 위한 목적이며,

저급 언어에서 고급 언어로 발전하는 과도기에, 현대적인 고급언어의 틀을 만들었다고 평가되며, 최근까지도 성능 최적화가 필요한 분야에서 주력으로 사용되는 언어입니다.

위와 같은 역사적 배경으로 인하여 고급 언어의 쉬운 난이도(당시 기준으로는 매우 쉬운 언어)와 저급 언어의 하드웨어 제어 기능을 모두 갖춘 뛰어난 구조적 프로그래밍 언어라는 평가를 받고 있습니다.

 

2. C++

C 언어를 기반으로 객체지향 프로그래밍 개념을 추가한 프로그래밍 언어입니다.

C 언어가 할 수 있는 것은 전부 처리할 수 있는 업그레이드 버전으로 생각할 수도 있겠지만,

객체지향 프로그래밍 방식의 과도기에 생겨난 언어로서, 절차 지향 언어의 극한이라 할 수 있는 C 언어에 억지로 객체지향 개념을 얹었기에 전문가도 헷갈릴만한 매우 복잡하고 어려운 언어라고 평가받는 상황입니다.

자체적인 코딩 규칙을 갖추고 익숙해지기만 하면 C 언어의 성능 이점과 객체지향의 생산성 이점을 동시에 얻을 수 있는 강력한 언어라고 생각합니다.(C/C++ 로 묶여서 취급받음)

 

3. Java

객체지향 프로그래밍을 위해 만들어진 프로그래밍 언어입니다.

말 그대로 객체지향 프로그래밍 개념을 성공적으로 이루어냈으며,

개발자 채용 시장에서 굉장한 비중을 차지하는 언어입니다.

앞서 C 와 C++ 는 작성한 코드를 기계어로 번역할 때, 실행할 환경별로 기계어를 번역할 CPU 등의 하드웨어 상황을 고려하여 번역기를 선정하는 등의 프로그램 설치에 불편함이 있는 반면, Java 는 기계가 직접 실행하는 언어가 아니라, 기계 위에 돌아가는 JVM 이라는 프로그램이 실시간으로 Java 의 중간 부산물인 ByteCode 라는 유사 기계어를 해석하여 실행시키는 방식으로, JVM 이 돌아가는 모든 종류의 디바이스에서 동일한 동작으로 실행됨을 보장한다는 장점이 있습니다.

최근에는 Java 에 Null 체크 기능이나 그외 코드 편의를 제공하는 Kotlin 언어도 사용되고 있습니다.

Java 는 JVM 이 실행해주고, 또한 C/C++ 에서 중요하게 처리해야하는 변수의 메모리 사용에 대한 내용을 처리할 필요가 없어진 만큼, JVM 이 자동으로 사용하지 않는 변수를 메모리상에서 제거해주는 Garbage Collector 라는 개념이 있으며, 이를 튜닝함으로서 시스템 전체 선능을 올리는 기법도 존재합니다.

 

4. JavaScript

웹 브라우저에서 표시되는 웹 서비스를 만들 때 필요한 3신기(HTML, CSS, Javascript) 중 하나입니다.

웹에서 '무엇을 하면 무엇을 해라'라는 프로그래밍적인 명령어를 작성할 때는 이외의 대안이 없습니다.

1995 년에 웹브라우저에서 동작하는 스크립트 언어로서 개발되었으며,

스크립트 언어란, 앞서 인터프리터 설명에서 말했듯, 프로그램 코드를 해석하고 프로그램 기능을 수행하는 프로그램인 인터프리터에 입력되어 인터프리터로 인해 실행되는 프로그래밍 언어를 뜻합니다.

웹 개발에서는 ReactJS, NextJS 등의 대표적이고 독보적인 웹 프레임워크의 주요 프로그래밍 언어로 사용되며,

웹브라우저에서 돌아가도록 설계된 언어지만, NodeJS 라는 기술로 인해, JS 의 인터프리터 언어를 브라우저가 아닌 로컬 프로그램으로 돌릴 수 있는 기술이 나왔기에 웹 화면 조작 용도뿐 아니라 서버 프로그램 구축용으로도 쓰일 수 있기 때문에 웹 화면과 웹 서버를 동시에 구축할 수 있는 능력자인 웹 풀스택 개발자를 노리는 분이시라면 이 언어 하나만을 익히셔도 무방하기에 JS 의 기술스택을 선택하시는 분들도 있습니다.

 

개인적으로는 프로그래밍 언어 하나만 잘 익히면 다른 언어를 익히는 것은 문제가 없기에 이에 얽매일 일은 없다고 생각하고 있으며, JS 는 쉽고 자유로운 대신 정확성이 떨어지는 느낌을 받는 경우가 많기에, 저는 JS 는 웹 개발시에만 사용하고 있습니다.

 

어쨌건 개발자로서는 웹 개발을 한다면 필수이고, 안하더라도 한번쯤 익혀둘만한 언어라고 소개드리며,

최근에는 JS 에는 없는 변수 타입 기능을 추가한 TypeScript 언어도 각광받고 있습니다.(TS 는 그대로 해석되서 실행되는 것이 아니라 빌드 시점에 일단 JS 로 빌드된 이후 실행되는 중간 과정이 있다는 특징이 있습니다.)

 

5. Python

네덜란드 국적 개발자인 귀도 반 로썸이 생업과는 별개의 취미로 만들었다는 일화로 유명한 언어입니다.

객체지향 언어이지만 객체지향 프로그래밍을 강제하는 자바와는 달리 꼭 객체지향을 따를 필요가 없으며, 문법의 구조가 매우 단순하고 배우기 쉬우며 이식성이 좋기에 개발자가 아닌 학자들에게도 인기가 좋은 언어입니다.

이러한 특성으로 인하여 현재는 딥러닝 연구개발의 주력으로서 사용되고, 그외의 최신 기술의 모태로 사용되고 있어 경쟁력이 좋은 언어입니다.

프로그래밍 언어적 특징으로는, Javascript 와 같은 스크립트 언어로, 인터프리터에 의해 실시간 해석되는 언어이지만,

JS 와 다른점으로는, Python 은 .py 파일이 일단 .pyc 파일의 바이트코드로 자바처럼 중간 해석을 거친다는 특징이 있습니다.

이렇게 바이트코드로 컴파일을 하는데 컴파일 언어가 아닌 인터프리터 언어로 분류되는 이유는,

자바처럼 컴파일 시점에 모든 코드를 해석하여 바이트 코드로 번역해서 실행하는 것이 아니라, 파이썬은 실시간으로 소스코드를 한줄씩 파이썬 바이트코드로 해석하고 바로 실행시키기 때문입니다.

 

6. PHP

HTML 에 포함되어 동작하는 서버측 스크립트 언어입니다.

C 언어와 유사한 문법 구조를 가지며, 객체지향 프로그래밍을 지원합니다.

웹 서비스 개발에 있어 주력으로 사용된 연혁이 긴 편이기에 다양한 라이브러리와 풍부한 커뮤니티 정보를 제공한다는 장점이 있지만, 비교적 보안에 취약하다는 단점이 있습니다.

현 시점 기업에서 활발히 사용되고 있지는 않지만, 기존 서비스 코드에서 자주 찾아볼 수 있으며, 아직 PHP 개발자에 대한 수요도 분명히 존재하고 있습니다.

 

- 이상입니다.

만약 정보처리기사 시험을 대비하자면 C, Java, Python 을 공부하시고,

개발자는 아니지만 프로그래밍의 능력을 익혀서 본인의 작업 스킬을 향상시키려면 Python 만을 익히셔도 좋습니다.

 

개인적으로 추천드리는 개발 공부의 순서는,

C 언어로 프로그래밍의 근본에 대해 이해하시고, Java 로 객체지향 프로그래밍 및 생계형 개발 스킬을 익히시며, JavaScript 를 익혀서 웹 개발의 기본적인 능력을 익힌 후, 관심에 따라 Python, PHP, C++ 등의 언어를 익히시는 것을 추천드립니다.

즉, C 와 Java 와 Javascript 는 어떤 개발자가 되시려고 하시건 한번쯤 공부하시는 것이 근본적 개발 능력에 좋은 영향을 줄 것이라고 추천드립니다.

 

[개발 환경 구성]

(하드웨어 환경 구성)

- 현대의 소프트웨어 구조는 클라이언트-서버 구조로 이루어지는 것이 대다수입니다.

서버는, 중앙 집중적으로 개발자가 속한 조직에서 준비한 컴퓨터 환경에서 데이터나 기능을 Serving 하는 역할을 하며,

클라이언트는 서비스를 이용하는 이용자의 컴퓨터 환경에서 서버에 데이터 및 기능을 요청하거나, 스스로 기능을 유저에게 제공하는 역할을 합니다.

 

- 서버측이건 클라이언트측이건 하드웨어 환경을 구성할 때 고려해야할 것은 운영 단계에서 동작할 하드웨어 환경과 되도록 유사한 것이 좋습니다.

특히나 클라이언트 측이라면 개발과 동시에 테스트를 진행하도록 다양한 디바이스와 다양한 런타임 환경을 준비해두는 것이 좋습니다.

 

- 서버의 종류는 아래와 같습니다.

1. 웹 서버 : 클라이언트에서 요청하는 정적 파일을 제공하는 서버입니다.

2. 웹 애플리케이션 서버(WAS) : 동적인 비즈니스 로직을 처리하고 그 결과를 제공하는 서버

3. 데이터베이스 서버 : DBMS 가 설치, 운영되는 서버

4. 파일 서버 : 파일 저장 및 공유를 위해 설치된 서버

 

- 위 서버의 종류 중 단골로 나오는 질문은 웹 서버와 WAS 의 차이를 설명하는 것입니다.

웹 서버는 앞단에 위치하는 클라이언트로부터 요청을 받아, 뒷단에 존재하는 HTML, CSS, JS 등의 정적 콘텐츠를 제공하는 서버이며, 웹 애플리케이션 서버는 스스로가 동적으로 DB 나 다른 파일 등에서 데이터를 가져오거나 하는 비즈니스 로직을 통해 동작하며, 그 결과를 동적으로 결합하여 반환하는 서버를 의미한다고 이해하면 됩니다.

 

예시로 보자면,

/index.html 을 요청시,

웹서버(Nginx) 의 경우는 파일을 그대로 전달하고,

 

/login 요청시,

WAS(Tomcat) 의 경우는 프로그래머가 구현한 사용자 인증 로직을 동적으로 수행하여 그 결과를 응답합니다.

 

(소프트웨어 환경 구성)

- 소프트웨어 개발에 필요한 개발 환경 구성은 개발자로서 어느 회사를 가든 가장 먼저 진행해야 할 업무입니다.

본인이 속한 팀과 업무 범위에 맞게 최적의 소프트웨어 개발 환경을 구축해야하며,

이에 대한 환경 구성 요소는 아래와 같은 종류가 있습니다.(전부 다 준비할 필요는 없습니다. 본인이 속한 직무에 맞는 도구만 사용하면 됩니다.)

 

1. 요구사항 관리 도구

목표 시스템의 기능과 제약 조건 등의 고객 요구사항을 수집, 분석, 추적하는 것을 지원하는 도구입니다.

JFeature, JRequisite, OSRMT, Trello 등이 있습니다.

 

2. 모델링 도구

기능의 논리적 결정을 위한 UML 지원, DB 설계 지원 등의 기능이 있는 도구입니다.

ArgoUML, DB Designer, StarUML, draw.io 등이 있습니다.

 

3. 소프트웨어 구현 도구

프로그램을 구현할 때의 주요 도구입니다.

IDE(통합 개발 환경, Integrated Development Environment) 가 가장 일반적이며, 이는 코딩, 코딩 시점 에러 발견, 코드 추천, 코드 파일 관리, 테스트, 빌드, 디버깅, 형상관리, DB 연결, 실행 등을 통합적으로 지원하는 도구입니다.

언어별, 프레임워크별, 목적별 다양한 프로그래밍 툴이 존재하며,

대표적으로는 Visual Studio(C/C++), Visual Studio Code(플러그인에 따라 여러 언어 지원), Eclipse(Spring Framework), IntelliJ(Spring Framework, 이외 여러가지), PyCharm(파이썬), Android Studio(AOS), XCode(MacOS) 등이 존재합니다.

최근에는 인공지능을 지원하는 IDE 도 떠오르고 있습니다.

 

4. 소프트웨어 테스트 도구

소프트웨어 품질을 높이기 위해 테스트에 사용되는 툴입니다.

코드 테스트, 성능 모니터링, 결과 리포팅, 분석 등의 기능을 제공합니다.

xUnit, STAF, Valgrind, JMeter 등이 있습니다.

 

5. 소프트웨어 형상관리 도구

개발자들이 생산해낸 소스, 리소스 등의 산출물에 대한 버전 관리를 위한 도구입니다.

게임으로 설명하자면, 진행 사항 세이브를 생각하시면 됩니다.

만약 게임을 켠 순간부터 진행하는 순간까지의 진행 결과가 저장되지 않는다면 게임을 끄고 다시 켠 경우나, 피치 못하게 컴퓨터가 꺼진 경우에는 복구가 불가능하듯, 프로그래밍 개발 진행의 상황도 중간 저장이 되지 않으면 무서운 일이 벌어지기에 이에 대한 기능 지원이 필요한 것입니다.

홀로 진행할 때의 백업용으로도 좋으며, 특히나 다수의 개발자가 한 프로젝트를 개발할 시에 일어날 수 있는 진행사항의 충돌 등을 효율적으로 해소하는 기능을 제공하기 때문에 협업에 필수적인 툴입니다.

Git, Subversion, CVS 등이 있습니다.

 

6. 소프트웨어 빌드 도구

개발자가 작성한 소스에 대한 빌드 및 배포를 지원하는 도구입니다.

C/C++ 와 같은 언어라면 CPU나 OS 등의 하드웨어적인 부분을 고려하여 빌드를 해야하며,

Java 의 경우에는 JVM 이 하드웨어단의 처리를 전담하므로 C/C++ 보다는 빌드가 쉽겠지만, 그럼에도 외부 라이브러리 등을 사용했을 때 각 라이브러리를 일일이 찾아서 적용해야하는 불편함이 있을 것입니다.

C/C++ 의 빌드 툴로는 Make, CMake, Meson, Bazel 등이 있으며, 이는 하드웨어적 환경에 따라 최적으로 빌드를 진행해줍니다.

Java 계열로는 Gradle, Maven 등이 있으며, 빌드용 파일 안에 해당 프로젝트가 어떤 라이브러리를 사용할 것인지를 작성만 한다면, 해당 라이브러리를 인터넷에서 가져와 적용하여 빌드를 해주고, 그 외 처리도 도와줍니다.

 

(배치 프로그램)

- 배치 프로그램이란,

미리 정해진 일련의 작업들을 정기적으로 반복 수행하거나 정해진 규칙에 따라 일괄 처리하는 프로그램입니다.

예를들어 매월 말일마다 이번달의 고객 주문 정보를 확인하여 우수 고객의 선정과 많이 팔린 제품의 선정 등을 하려고 할 때,

매월 말일이라는 정기적인 시점마다 데이터를 조회하고, 계산하고, 그 통계 결과를 저장하는 일련의 작업을 수행하는 것을 자동화하는 프로그램이 있을 때, 이를 배치 프로그램이라고 합니다.

 

- 배치 프로그램의 필수 요소는 아래와 같습니다.

1. 대용량 데이터 처리 : 작은 데이터라면 일괄 처리할 필요 없이 실시간 처리를 하면 되기에 배치 프로그램은 대부분 대용량 데이터를 처리하는 것이 전제입니다.

2. 자동화 : 정해진 시간에 정해진 작업을 자동적으로 수행하여 수고를 대신해야 합니다.

3. 견고함 : 작업 수행시 일어날 수 있는 상정하지 못한 상황으로 인해 작업이 끊기거나 폭주해서는 안됩니다.

4. 안정성 : 배치 프로그램은 사람의 터치가 없는 시점과 환경에서 사람의 판단 없이 많은 작업을 대신해야 하기 때문에 에러가 발생하지 않아야 하며, 만약 에러가 발생한 경우에도 논리적으로 허용될 처리를 수행해야만 합니다.

 

- 배치 프로그램은 대부분 서버 개발자가 담당하게 됩니다.

고로 Spring Framework 계열의 기술에 이를 지원하는 기술이 있으며,

Spring Batch(대용량 데이터 처리), Spring Quartz(일정 시간에 따라 특정 작업을 수행하는 스케쥴링) 가 있습니다.

 

[프로그래밍 패러다임]

(절차형 프로그래밍)

- 절차형 프로그래밍은 가장 근본적인 프로그래밍 방법론이라고 할 수 있습니다.

책이 있다고 할 때, 내용은 좌측에서 우측으로, 상단에서 하단으로 진행될 것입니다.

직장 상사가 본인에게 내리는 지시사항을 문서로 보냈다고 합시다.

우리는 이를 기반으로 작업을 수행하게 되겠죠?

 

프로그래밍도 이와 같이 미리 작성된 규칙에 따라 좌측에서 우측으로, 상단에서 하단으로 진행되는 흐름에 따라 차례차례 순차적으로 명령이 수행되게 됩니다.

이것이 바로 절차형 프로그래밍 방법론입니다.

 

모든 프로그래밍 언어는 절차적으로 진행이 되며, 대표적으로 C 언어의 경우는 컴퓨터에 명령을 내리기 위한 고급 언어 명령 체계를 처음으로 제대로 정립한 언어이기에 절차형 프로그래밍 언어의 대표라고 할 수 있습니다.

 

- 쉽게 말하자면,

'이럴 땐 이걸 해라, 저럴 땐 저걸 해라' 라는 명령문을 나열하는 것이라고 생각하시면 됩니다.

 

(객체지향 프로그래밍)

- 객체지향 프로그래밍(OOP, Object-Oriented Programming)은 실제 세계를 모델링하기 위해 고안된 프로그래밍 패러다임입니다.
OOP의 핵심은 "데이터(상태)"와 "그 데이터에 대한 동작(행위)"을 **객체(object)**라는 단위로 묶는 것입니다.

 

- 쉽게 말하자면, 객체라는 것을, 변수 + 함수의 묶음으로 보고, 그러한 객체들을 만들고 조합하는 방식으로 개발을 진행해 나가는 것입니다. 프로그램의 가장 바깥에도 객체가 있고, 객체 안에 도 객체가 있으며, 그 안에도 객체가 존재합니다.

이러한 식으로 프로그래밍 단위를 객체로 나누어 생각하는 것의 장점은, 인간적인 사고방식에서 코드를 이해하기 쉬우며, 코드의 분할을 통한 모듈화, 그로인한 유연성과 유지보수성과 재활용성의 장점을 쉽게 이룰 수 있게 해줍니다.

 

- 절차지향 프로그래밍에 비해 컴퓨터 본연의 동작과는 차이가 존재하는 방법론이기에 성능상 최적화를 기대할 수는 없지만, 그외의 생산성과 유지보수성 등의 매우 큰 장점들이 있기에 현 시대 프로그래밍 방법론의 주류라고 할 수 있으며,

대표적으로는 Java, Python, C++ 등이 있습니다.

 

- 객체 지향 프로그래밍에서의 몇가지 개념을 알아보겠습니다.

1. 객체(Object) : 설계 단계에서 정의한 개체의 단위입니다.

아직 코드도 실체도 없는 단계에서 이것을 만들겠다고 설계한 상태입니다.

 

2. 클래스(Class) : 객체를 이루고 있는 속성값(ex : 나이, 직업, 성별 등과 같은 속성)과, 객체가 행하는 행위(ex : 거북이 개체는 '기어다닌다', '수영한다' 와 같은 기능을 수행합니다.) 를 코드로 작성한 것입니다.

아직 실체는 없으며, 실체를 만들기 위한 설계도라고 보시면 됩니다.

 

3. 인스턴스(Instance) : 클래스를 기반으로 만들어낸 실체입니다. 예를들어 '사람' 클래스가 있을 때, 이를 기반으로 '철수' 와 '영희'라는 개개별의 실체를 만들어 낼 수 있습니다.

 

위의 중요한 개념인 클래스와 인스턴스라는 개념을 자바 코드로 예를 들겠습니다.

// Person.java
public class Person {
    // 속성 (필드)
    private String name;
    private int age;

    // 생성자 (Constructor)
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 동작 (메서드)
    public void introduce() {
        System.out.println("안녕하세요, 제 이름은 " + name + "이고 나이는 " + age + "살입니다.");
    }
}

 

위와 같이 사람에 대한 클래스를 설정하였습니다.

보시다시피 사람을 이루는 가장 기본적인 정보로 이름과 나이 정도를 설정하여 클래스 내부의 변수(멤버변수)로 선언하였고,

사람이 행하는 행위 중 자기 소개 정도를 설정하였습니다.

 

외부에서 이 클래스를 가지고 실체를 만들어 이용하려면,

// Main.java
public class Main {
    public static void main(String[] args) {
        // 철수와 영희 인스턴스 생성
        Person chulsoo = new Person("철수", 20);
        Person younghee = new Person("영희", 18);

        // 메서드 호출
        chulsoo.introduce();   // 출력: 안녕하세요, 제 이름은 철수이고 나이는 20살입니다.
        younghee.introduce();  // 출력: 안녕하세요, 제 이름은 영희이고 나이는 18살입니다.
    }
}

 

위와 같이 new Person 의 방식으로 철수와 영희의 각각의 인스턴스를 만들고,

그렇게 얻어온 객체 변수 안에 설정되어 있던 introduce 메소드(클래스에 포함된 함수)를 실행시키면,

해당 객체가 동작하게 되는 것입니다.

 

- 클래스 내에서 클래스에 소속되에 선언된 변수를 멤버변수라고 부르며, 클래스에 소속되어 클래스의 행위를 나타내는 함수를 메소드(Method)라고 부릅니다.

실무에서는 잘 쓰이지 않는 용어지만, 객체간 상호작용을 위해 주고받는 데이터를 메시지라고합니다.

 

- 객체 지향은 아래와 같은 특징이 있습니다.

1. 캡슐화(Encapsulation)

객체의 상태(데이터)를 외부에서 직접 접근하지 못하게 하고, 메서드를 통해서만 접근하게 하는 것으로,

데이터 보호 및 코드 유지보수가 용이합니다.

public class Person {
    private String name;  // 외부에서 직접 접근 불가

    public void setName(String name) {  // 외부에서 수정할 수 있는 메서드
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

 

2. 정보은닉(Information Hiding)

내부 구현을 감추고, 필요한 인터페이스만 외부에 제공하는 것으로, 캡슐화의 목적 중 하나이자 결과입니다.

변경이 쉬운 구조 설계 및, 외부 의존이 최소화됩니다.

public class BankAccount {
    private int balance = 0;

    public void deposit(int amount) {
        if (amount > 0) balance += amount;
    }

    public int getBalance() {
        return balance;
    }
}

 

3. 추상화(Abstract)

공통된 속성과 동작을 추출하여 필요한 것만 드러내고 불필요한 것은 숨기는 것으로, 복잡성을 줄이고 핵심에 집중합니다.

abstract class Animal {
    abstract void makeSound();  // 구체적인 동작은 숨김
}

class Dog extends Animal {
    void makeSound() {
        System.out.println("멍멍!");
    }
}

 

4. 상속(Inheritance)

기존 클래스(부모)의 속성과 동작을 새로운 클래스(자식)가 물려받는 것으로, 코드의 재사용과 계층 구조 형성에 유리합니다.

class Animal {
    void eat() {
        System.out.println("먹는다");
    }
}

class Cat extends Animal {
    void meow() {
        System.out.println("야옹~");
    }
}

 

5. 다형성(Polymorphism)

동일한 인터페이스나 메서드가 다양한 방식으로 동작할 수 있도록 하는 것으로, 유연한 코드 구성이 가능해집니다.

class Animal {
    void sound() {
        System.out.println("동물 소리");
    }
}

class Dog extends Animal {
    void sound() {
        System.out.println("멍멍!");
    }
}

class Cat extends Animal {
    void sound() {
        System.out.println("야옹!");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal a1 = new Dog();
        Animal a2 = new Cat();
        a1.sound();  // 멍멍!
        a2.sound();  // 야옹!
    }
}

 

- 객체지향 프로그래밍은 현 시점 주된 개발 방법론입니다.

프로그램의 각 요소를 현실적인 객체로 설계해야 하므로 주관적이며 다양할 수 있는데,

좋은 객체 설계를 위한 아래와 같은 객체지향 분석 방법론(시스템을 객체를 중심으로 분석하고 설계하는 소프트웨어 개발 방법론)이 존재합니다.(자세한 설명은 따로 찾아보시길 바랍니다.)

 

1. Rumbaugh(럼바우)

소프트웨어의 구성 요소를 다양한 그래픽 표기법을이용하여 모델링하는 기법.

가장 일반적으로 사용되는 방법으로, 아래와 같은 순서로 진행됩니다.

_객체 모델링 : 객체 다이어그램을 활용하여 객체와 객체간의 관계 정의

_동적 모델링 : 상태, 활동 다이어그램을 활용하여 기능의 흐름을 표시

_기능 모델링 : 자료 흐름도(DFD)를 활용하여 입출력 데이터, 세부 기능 결정

 

2. Booch

미시적 개발 프로세스와 거시적 개발 프로세스를 모두 사용하는 분석 기법입니다.

클래스와 객체들을 분석 및 식별하고 클래스의 속성과 연산을 정의합니다.

 

3. Jacobson

사용자와 시스템이 상호작용하는 시나리오(Use-Case)를 활용하여 분석하는 기법입니다.

 

4. Coad & Yourdon

E-R 다이어그램을 사용하여 객체의 행위를 모델링하는 분석 기법입니다.

객체 식별, 구조 식별, 주제, 속성과 인스턴스 연결, 연산과 메시지 연결 등을 정의합니다.

 

5. Wirfs-Brock

분석과 설계 간 구분이 없으며 고객 명세서를 평가하여 설계 작업까지 연속적으로 수행하는 분석 기법입니다.

 

- 어느 프로그래밍이든 프로그램 설계 방식은 다양할 수 있고, 코딩 방식도 다양할 수 있습니다.

실무에서는 어떤 프로그램에 투입되었을 때, 해당 업무의 책임자의 방식을 따라야 하는데, 이 경우 해당 방식이 비 합리적일 경우, 나와 많이 다를 경우 괴로워지죠.

논리적으로 생각 했을 때의 좋은 설계에 대한 기준이 있다면 이를 통해 상대를 설득하거나, 혹은 애초에 상호간 설계 및 개발 방법을 유사하게 하여 갈등을 최소화 시킬 수 있을 것입니다.

 

좋은 객체지향 설계에 대한 원칙으로는 SOLID 가 있습니다.

1. 단일 책임(Single Responsibility)

하나의 클래스가 제공하는 모든 기능이 하나의 문제만 해결하도록 설계해야 합니다.(왠만하면 함수도 동일)

하나의 문제 해결을 위해서만 클래스가 변경되므로 낮은 결합도, 높은 응집도가 보장됩니다.

public class UserService {
    public void registerUser(String username) {
        // 사용자 등록 로직
    }
}

public class EmailService {
    public void sendWelcomeEmail(String email) {
        // 이메일 전송 로직
    }
}

 

위와 같이 각 클래스별 주제에 맞는 함수만 작성해야 합니다.

 

2. 개방 폐쇄(Open-Closed)

기능 확장에 대해선 개방적이어야 하며, 수정에 대해서는 폐쇄적이어야 합니다.

기존 코드의 수정 없이 기능을 추가할 수 있는 확장성과, 사전 설계가 변경되지 않는 안정성이 보장됩니다.

 

어떠한 기능을 설계할 때에, 해당 기능에 대한 요구사항이 요구된 시점에서 기능구현만을 고려하는 것이 아니라,

추후 기능 확장이 있을 것이라고 가정하여 설계를 해야하며,

그러기 위해선 되도록 추상화를 잘 하고, 핵심 부분에 대한 단일 책임 원칙을 지킨다면 개방 폐쇄에 유리한 설계가 될 것입니다.

 

3. 리스코프 치환(Liskov Substitution)

1987 년 Barbara Liskov 가 처음 제안하였기에 이러한 이름으로 명명되었으며,

부모 클래스(또는 인터페이스)를 사용하는 곳에 자식 클래스를 넣어도 프로그램의 동작에 문제가 없어야 한다는 원칙입니다.

 

예를들어,

class Bird:
    def fly(self):
        print("날고 있어요!")

class Ostrich(Bird):
    def fly(self):
        raise NotImplementedError("타조는 날 수 없어요!")

 

위와 같이 Bird 클래스를 기반으로 Ostrich 객체를 설계했다고 했을 때, 이를 사용하면,

def make_it_fly(bird: Bird):
    bird.fly()

make_it_fly(Ostrich())  # 예외 발생! LSP 위반!

 

위와 같이 날 수 없는 새인 타조가 날려고 할 때에 예외가 발생할 것입니다.

 

물론, 상속하는 위치에서 상위 메소드를 오버라이딩하여 예외 처리를 하면 될테지만, 이렇게 어떤 하위 클래스는 상위 클래스의 메소드를 사용하지 않는 식으로 따로 처리를 해야 하는 것은 설계상 좋지 못하다고 할 수 있습니다.

 

위와 같은 상황에서는 Bird 에 fly 메소드를 제거하고, 대신 Flyable 이라는 인터페이스를 또 따로 분리하여, 날 수 있는 새는 이를 상속받고, 아닌 새는 이를 상속받지 않는 방식으로, 상속 시점에 사용 가능 여부를 판별해야 하는 문제를 해결 할 수 있습니다.

 

4. 인터페이스 분리(Interface Segregation)

하나의 포괄적인 인터페이스보다는 다수의 구체적인 인터페이스를 구성하는 것이 좋습니다.

또한 사용하지 않는 인터페이스는 구현하지 말아야 합니다.

이는 단일 책임 원칙과도 비슷하며, 요는 확실하고 명확한 인터페이스를 추구하여 인터페이스 사용 시점의 헷갈림을 방지하자는 의미입니다.

 

5. 의존성 뒤집기(Dependency Inversion)

하위 클래스의 변경 사항이 상위 클래스에 영향을 미치지 않도록 구성해야 합니다.

즉, 상위 클래스는 하위 클래스를 모르는 상태로 독립이 가능해야 하는 것을 준수해야합니다.


[코딩 요소]

- 간단히 코딩에 필수적인 기본 개념들을 간략하게 짚고 넘어가겠습니다.

말씀드렸듯, 프로그래밍은 정보처리 기사 시험을 위해서라면 C, Java 를 정식으로 배우시길 추천드리며,

위 두 언어(절차지향 + 하드웨어 이해, 객체지향)를 이해한다면 Python, Javascript, Go, Kotlin 등의 여타 다른 프로그래밍 언어는 얼마든 짧은 시간 내에 쉽게 익히실 수 있을 것입니다.

 

아래 설명글은 프로그래밍을 이해하신 분들에게 환기를 시켜드리기 위한 용도입니다.

 

(변수지정)
- 프로그래밍 언어는 변수를 지정하여 논리적으로 활용합니다.

변수를 선언한다는 것은, 코드 흐름상 다음 부분에서부터는 해당 값을 해당 이름으로 사용한다는 의미이며,

예시는 아래와 같습니다.

int age = 25;          // 정수형 변수
float height = 175.5f; // 실수형 변수 (f 접미사 필수)
char grade = 'A';      // 문자형 변수
char name[] = "철수";   // 문자열 (문자 배열)


printf("이름: %s\n", name);
printf("나이: %d세\n", age);
printf("키: %.1fcm\n", height);
printf("성적: %c\n", grade);
printf("학생 여부: %s\n", isStudent ? "예" : "아니오");

 

위에서 선언한 각 변수를 아래쪽에서 printf 로 출력에 활용하는 것입니다.

 

참고로 위의 예시에서는 변수의 선언과 할당을 동시에 했는데,

// 선언 (Declaration)
int age;

// 할당 (Assignment)
age = 20;

// 출력
System.out.println("나이: " + age);  // 출력: 나이: 20

// 재할당 (Reassignment)
age = 25;

// 출력
System.out.println("바뀐 나이: " + age);  // 출력: 바뀐 나이: 25

 

위와 같이 선언, 할당, 재할당을 각각 수행할 수 있습니다.

 

- 변수의 타입

위 예시에서 보듯 각 변수는 int, char, float 등의 다양한 타입을 지정할 수 있습니다.

이 의미는 컴퓨터 메모리상에 할당할 메모리 공간의 사이즈와, 해당 메모리 공간에 있는 값을 어떤 방식으로 해석할지에 대한 메타 정보라고 할 수 있습니다.

 

예를들어 실제 메모리 공간 1111 번 주소에 있는 데이터를 조회할 때, 이 시작 주소에서부터 어디까지를 한 데이터 묶음으로 볼 것인지, 이것을 정수 숫자로 볼 것인지, 실수 숫자로 볼 것인지, 문자로 볼 것인지 등에 대한 정보가 있어야 해석이 가능할 것입니다.

 

적절한 변수 타입을 지정해야만 합니다. 예를들어 매우 중요한 데이터 계산시 정밀도가 낮은 실수 타입을 사용하여 그 결과 차이가 허용범위를 넘어가거나, 사이즈가 작은 정수 타입을 사용하였을 때, 매우 큰 값이 들어가서 오버플로우가 된다면 그 문제가 현실적인 손해를 끼칠 문제가 될 수도 있습니다.

 

- 상수

변수는 변할 수 있는 수, 상수는 변할 수 없는 수입니다.

프로그래밍 변수는 위와 같이 변수 타입, 변수명을 지정하고 해당 변수명에 할당된 공간에 값을 집어넣어 활용을 하는데, 변수 값을 재할당하여 같은 변수명으로도 조회될 수 있는 값을 변경할 수 있습니다.(위의 선언, 할당, 재할당 예시)

 

하지만 

const int MAX_AGE = 100;  // 상수 선언

 

이렇게 const 로 지정하여 상수화한 변수(상수)는 한번 할당된 값을 변경할 수 없습니다.

특정한 값에 '이름을 붙인다'는 의미이며,

굳이 상수화를 하지 않아도 상관 없지만, 변하지 않는 값에 상수 제약을 둠으로써 변경하지 않아야 할 값을 변경하는데서 오는 에러를 방지할 수 있습니다.

 

- 프로그래밍 언어별 변수 선언 규칙이 존재합니다.

프로그래밍 코드로 이미 저장된 예약어를 사용하지 못하며, 특수문자를 사용하지 못하는 등의 제약이 있을 수 있습니다.

언어 자체적으로 제약한 변수명 규칙 외에도 개발자들 사이에서, 사용하는 기술의 범위 내에서, 각 조직의 안에서 권고되는 규칙이 있을 수도 있으므로 유연하게 대처하시면 되며,

가장 중요한 원칙으로는, 코드는 '남에게 보여주고 이해시키는 것'이 중요하다는 것을 잊지 맙시다.

아무리 홀로 진행하는 코드라도 이를 준수하는 것이 좋으며, 특히나 조직에서 협업시 개발 업무는 의사소통의 업무라고 생각하며, 변수명도 그렇게 작성하셔야 합니다.

 

각 조직별 규칙이 존재할테지만, 일단 제 기준을 말씀드리자면, 해당 변수가 담당하는 핵심 영역을 단번에 알 수 있으며, 동일한 핵심 영역을 담당하는 또다른 변수가 생길 수도 있다는 것을 고려해야 하고, 되도록 영문 약어(ex : count -> cnt)로 변수명을 줄이는 것을 추천드립니다.

 

(자료구조)

- 프로그래밍 언어에는 기본으로 제공되는 int, char, float 등의 기본 타입 및 각 기본타입의 배열을 사용하여, 리스트, Set, Map 등의 자료구조를 만들어 낼 수 있습니다.

직접 만드는 것 외에도 각 언어별 기본 제공되는 자료구조도 있으므로 이를 잘 활용하면 좋습니다.

 

- 예를 들어 몇가지만 보겠습니다.

자바에서 리스트는,

import java.util.ArrayList;
import java.util.List;

public class ListExample {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>();
        
        // 요소 추가
        fruits.add("사과");
        fruits.add("바나나");
        fruits.add("사과");  // 중복 허용
        
        // 출력
        for (String fruit : fruits) {
            System.out.println(fruit);
        }
    }
}

 

Set 은,

import java.util.HashSet;
import java.util.Set;

public class SetExample {
    public static void main(String[] args) {
        Set<String> fruits = new HashSet<>();
        
        // 요소 추가
        fruits.add("사과");
        fruits.add("바나나");
        fruits.add("사과");  // 중복 무시
        
        // 출력
        for (String fruit : fruits) {
            System.out.println(fruit);
        }
    }
}

 

이런식으로 사용할 수 있습니다.

 

- 이외에 C 에서는 여러 변수들을 한대 묶은 구조체와 같은 자료형도 있으므로 자료구조를 잘 활용하는 것이 좋습니다.

첨언하자면, C 에서의 자료구조는 실제 메모리상 저장되는 구조나 사용하는 메모리 공간 할당, 메모리에서 해제하는 로직 등을 고려해야 하고,

단순 조회시에도 메모리 주소를 신경써야 하는 등 현대적 프로그래밍 언어들 중에서 독보적으로 어려운 언어라고 할 수 있습니다.

 

반면 자바는 기본적으로 제공되는 자료구조가 객체지향적으로 간단히 사용 가능하게 제공이 되지만, 내부 로직을 직접 최적화할 수는 없기에 다양한 타입의 형태로 제공되므로 용도에 맞게 적절한 자료구조 데이터 타입을 사용해야 합니다.


(연산자)
- 연산자는 하나 또는 그 이상의 데이터를 연산하여 새로운 결과값을 만들어내는 코딩 요소입니다.

두 변수를 더하는 +, 두 변수를 곱하는 *, 한 변수의 값을 1 올리는 ++, 변수 데이터의 비트값을 좌측으로 한칸씩 이동시키는 << 등이 존재하며, 예를들면,

#include <stdio.h>

int main() {
    int a = 10;
    int b = 20;
    int sum = a + b;

    printf("정수 덧셈: %d + %d = %d\n", a, b, sum);
    return 0;
}

 

위와 같습니다.

 

- 위의 예시와 같이 연산에 두 변수를 필요로하는 연산자를 이항 연산자,

++a; // 위 예시에서는 a 값이 11 이 됨

위와 같이 변수가 하나만 필요한 연산자는 단항 연산자,

#include <stdio.h>

int main() {
    int score = 85;

    // 삼항 연산자 사용: 조건 ? 참 : 거짓
    char* result = (score >= 60) ? "합격" : "불합격";

    printf("결과: %s\n", result);  // 출력: 결과: 합격

    return 0;
}

 

이처럼 항이 3개 필요한 삼항 연산자까지 존재합니다.

 

- 연산자 중에서는 데이터 타입을 따지는 연산자가 있습니다.

위의 예시와 같이 숫자 값에만 동작하거나, 참/거짓의 Boolean 값에만 동작하는 연산자가 있죠.

일반적으로 이항연산자는 각 변수가 같은 타입이어야 하며, 연산 결과값 역시 제공되는 타입이 다를 수 있기에 주의해야 합니다.

 

예를들어,

#include <stdio.h>

int main() {
    int a = 5;
    int b = 2;
    float result = (float)a / b;  // (float)5 / 2 → 5.0 / 2 → 2.5

    printf("결과 (캐스팅 적용): %f\n", result);  // 출력: 2.500000
    return 0;
}

 

위에서 보시는바와 변수는 정수값이지만, / 연산의 결과값은 실수가 나오는 경우, 위와 같이 하나라도 변수를 실수값으로 변환해주면, 다른쪽 값도 자동으로 변환되어서 결과값이 실수로 잘 나오는 것을 볼 수 있습니다.

만약 위에서 변수 타입을 변경하지 않았으면 / 연산의 결과값은 자동으로 정수값으로 잘려버려서 그 결과값이 2.0 으로 출력될 것입니다.

 

위에서 보시는 바와 같이 a 의 타입을 변환하는 것을 강제 타입 캐스팅,

같이 연산된 b 가 실수값으로 계산되기 위해 내부적으로 자동으로 실수로 변환되는 것을 자동 타입 캐스팅(데이터 표현 범위가 작은 것에서 높은 쪽으로 변환됩니다.)이라 부르며, 이를 주의해서 연산을 해야 합니다.

 

- 각 연산자는 우선순위가 존재합니다.

수학에서 덧셈 뺄샘과 나눗셈 곱셈의 우선순위가 다르듯, 코딩에서도 이 우선순위에 따라 연산자가 적용되는 순서와 최종 결과가 달라지므로 주의해야 합니다.


(조건문)
- 조건 분기는 프로그래밍의 가장 중요한 기능 중 하나입니다.

어떤 상황일 때에는 이렇게 행동하고, 또 다른 상황에서는 저렇게 행동하는 식으로 프로그램이 선형으로만 움직이는 것이 아닌 보다 다양하게 동작할 수 있게 해주는 요소입니다.

#include <stdio.h>

int main() {
    int num = 10;

    if (num > 0) {
        printf("양수입니다.\n");
    }

    return 0;
}

 

위와 같이 num 변수가 양수인지 음수인지의 조건으로 수행되는 동작을 지정할 수 있습니다.

 

- 동일 변수를 기준으로 여러 분기를 만들 때에는,

#include <stdio.h>

int main() {
    int menu = 2;

    if (menu == 1) {
        printf("짜장면 선택\n");
    } else if (menu == 2) {
        printf("짬뽕 선택\n");
    } else if (menu == 3) {
        printf("탕수육 선택\n");
    } else {
        printf("잘못된 메뉴 선택\n");
    }

    return 0;
}

 

이렇게 해도 되지만,

#include <stdio.h>

int main() {
    int menu = 2;

    switch (menu) {
        case 1:
            printf("짜장면 선택\n");
            break;
        case 2:
            printf("짬뽕 선택\n");
            break;
        case 3:
            printf("탕수육 선택\n");
            break;
        default:
            printf("잘못된 메뉴 선택\n");
    }

    return 0;
}

 

이렇게 switch 문으로 작성하는 것이 더 깔끔할 수 있습니다.

 

참고로, C 언어에서의 switch 문의 결과값을 예측하는 것은 프로그래밍 문제의 단골 주제입니다.

왜냐면, 위 예시에서 모든 case 에서 break 가 없다고 가정했을 때에는,

그 결과가 '짬뽕 선택' 만 출력되는 것이 아니라,

 

'짬뽕 선택'

'탕수육 선택'

'잘못된 메뉴 선택'

 

으로, 선택된 case 로 부터 아래의 case 가 전부 수행되기 때문에 함정을 심기 좋으며, C 언어를 사용한지 오래된 개발자 역시 헷갈리기 쉽기 때문입니다.


(반복문)
- 프로그래밍의 주요 기능에서 조건문과 동일하게 가장 중요하게 사용되는 기능입니다.

제 개인적인 생각으로는, 조건문인 인간의 지능을 표방하기 위한 기능이라면, 반복문은 기계적인 특성을 활용하기 위한 기능이라 생각합니다.

생각과 분류가 사람이 잘하는 영역이라면, 반복은 기계가 잘하는 영역이기 때문이죠.

 

- C 언어에서 반복문은 아래와 같이 작성합니다.

#include <stdio.h>

int main() {
    // 1부터 5까지 출력
    for (int i = 1; i <= 5; i++) {
        printf("%d\n", i);
    }

    return 0;
}


(함수)
- 함수란, 필요할 때에 특정 기능을 수행하도록 작성된 일종의 작은 프로그램입니다.

재활용 가능한 코드 뭉치라고 생각해도 되며, 아래와 같습니다.

#include <stdio.h>

// 함수 선언 (선언부)
int add(int a, int b);

// 메인 함수
int main() {
    int result = add(3, 5);  // 함수 호출
    printf("3 + 5 = %d\n", result);
    return 0;
}

// 함수 정의 (구현부)
int add(int a, int b) {
    return a + b;
}

 

위와 같이 add 함수를 선언 및 구현하고,

이를 호출하여 함수 내에 작성된 코드를 실행합니다.

 

- 위 함수 선언부에서 보실 수 있듯, 사용자 입장에서 중요한 것은 함수가 어떤 값을 필요로 하는지(함수 파라미터), 함수가 실행된 결과가 어떤 값을 반환하는지(함수 반환값), 그리고 해당 함수를 호출하기 위해선 어떤 이름으로 호출해야 하는지(함수명)가 필요하며, 이러한 요소만 모이면 사용자 입장에서는 구현을 어떻게 하는지는 신경을 쓰지 않아도 됩니다.

 

외부에서 보이는 함수 형식을 함수 인터페이스라고 부르며, 개발자 입장에서는 인터페이스를 먼저 선언하고 구현을 나중으로 미루는 방식으로 개발을 진행할 수도 있으며,

사용자 입장에서는 공개된 인터페이스를 기준으로 프로그래밍 진행을 병렬적으로 진행할 수도 있습니다.

 

- C 언어에서 함수 호출 방식은 다음 두 가지로 나눌 수 있습니다:
Call by Value (값에 의한 호출): 원본 변수의 값이 아닌 복사된 값이 함수로 전달됨
Call by Reference (참조에 의한 호출): **주소(포인터)**를 전달하여 함수 내에서 원본 값 수정 가능

 

예를들어 Call by Value 는,

#include <stdio.h>

void changeValue(int x) {
    x = 100;  // x는 복사된 값이므로 원본에는 영향 없음
}

int main() {
    int a = 10;
    changeValue(a);
    printf("Call by Value 결과: %d\n", a);  // 출력: 10
    return 0;
}

 

위와 같이 함수의 매개변수를 값으로 받아오므로 함수 내에서 매개변수 값을 변경해도 함수 외부에는 변함이 없지만,

 

Call by Reference 는,

#include <stdio.h>

void changeValue(int *x) {
    *x = 100;  // 포인터를 통해 원본 값 변경
}

int main() {
    int a = 10;
    changeValue(&a);
    printf("Call by Reference 결과: %d\n", a);  // 출력: 100
    return 0;
}

 

위와 같이 값이 아닌 해당 변수에 접근하기 위한 주소를 전달하기에 함수 내부에서 변수 값을 변경하면 해당 변수를 입력한 함수 외부의 변수에서도 값이 변경되므로 이 개념을 주의해야합니다.


(예외처리)
- 컴퓨터는 논리적이고 정확하고 빠르지만 인간만큼 유연하지는 못합니다.

고로 컴퓨터에 명령을 내릴 때는 정확하고 치밀해야만 하며, 만약 명령에 논리적 모순이 생긴다면 생각하지도 못한 오동작을 일으킬 가능성이 있습니다.

 

- 개발 초보의 입장에서 프로그래밍의 가장 무서운 상황으로는, 완성된 프로그램이 사용되는 도중에 빨간 글씨가 뜨면서 에러 표시가 나는 것으로 생각할 것이지만,

사실 그보다 더 무서운 상황은, 에러 표시도 뜨지 않고 프로그램이 멈추지도 않지만, 내부적으로는 의도치 않은 동작이 조용하게 실행되는 상황이 더욱 무서운 상황입니다.

 

- 코딩을 진행하면서 프로그래밍 언어에서 규정한 논리에는 벗어나지 않지만, 본인이나 조직에서 결정한 정책 사항에 위배되는 경우 이를 진행 못하게 하고 싶은 경우가 있습니다.

조건문을 사용하여 일일이 이에 대응하는 방식도 있겠지만,

그러한 처리를 할 시간이 없거나, 혹은 그럴만한 가치를 못느끼는 경우는 '프로그램 예외'를 던져서 이를 실행시키는 디바이스와 OS 자체적으로 프로그램을 멈추고 경고문을 띄워주게 할 수 있습니다.

// 사용자 정의 예외 클래스
class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
}

// 예외를 던지는 메서드
public class ExceptionExample {
    public static void validateAge(int age) throws CustomException {
        if (age < 18) {
            throw new CustomException("18세 미만은 허용되지 않습니다.");
        } else {
            System.out.println("나이 확인 완료: " + age);
        }
    }
}

 

Java 에서는 위와 같이 Exception 객체를 throw 함으로써 원치 않는 상황에 예외가 발생하게 할 수 있습니다.

 

- 본인이 발생시키거나, 타인이 작성한 함수를 잘못 사용하여 발생한 예외에 대해, 프로그램을 멈추지 않고 별도의 처리를 하고 싶은 경우는 아래와 같이 처리를 할 수도 있습니다.

public class TryCatchFinallyExample {
    public static void main(String[] args) {
        int numerator = 10;
        int denominator = 0;

        try {
            // 예외가 발생할 가능성이 있는 코드
            int result = numerator / denominator;
            System.out.println("결과: " + result);
        } catch (ArithmeticException e) {
            // 예외가 발생했을 때 처리
            System.out.println("예외 발생: 0으로 나눌 수 없습니다.");
        } finally {
            // 예외 발생 여부와 상관없이 항상 실행
            System.out.println("finally 블록 실행됨.");
        }

        System.out.println("프로그램 계속 실행됨.");
    }
}

 

위와 같이 Exception 이 일어날 것 같은 부분을 try 블록으로 감싸면,

이 블록 내에서 catch 에 설정한 유형의 예외가 발생할시, catch 블록의 코드가 실행되고,

finally 블록의 경우는 예외가 발생했건 하지 않았건 무조건 try 나 catch 블록 마지막에 실행됩니다.

 

(스레드)

- 스레드란, 하나의 프로세스에서 둘 이상의 일을 동시에 수행하는 것을 말합니다.

프로그램은 코딩 문서를 한줄한줄 읽어나가며 순차적으로 하나의 흐름으로 진행되는데,

때로는 동시에 둘 이상의 흐름으로 실행되도록 하고 싶을 때가 있습니다.

예를들면 게임에서 내 캐릭터가 움직이는 동시에 상대방도 움직이고, 환경도 변하는 등의 기능을 구현하려 할 때가 그렇습니다.

 

- 프로그래밍 흐름이 둘 이상이 되면 신경써야할 요소가 많이 존재합니다.

둘 이상의 흐름이 동시에 동일 변수의 값을 변경하려 할때나,

물리적으로 나눌 수 없는 하나의 리소스를 동시에 요구할 때의 처리와 같은 것을 신경써야 하며,

이를 프로그래밍 병렬처리라고 합니다.

 

여기서 이 이상 자세한 내용을 설명하지는 않겠지만, 더 자세한 내용을 알고 싶으시면 운영체제, 시스템 프로그래밍, 병렬 프로그래밍 등을 공부해보세요.

 

- Java 의 멀티 스레드 예시는 아래와 같습니다.

class MyRunnable implements Runnable {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("Runnable: " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadExample2 {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyRunnable());
        t1.start();

        for (int i = 0; i < 5; i++) {
            System.out.println("main thread: " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

위 코드를 실행하면,

main thread: 0
MyThread: 0
main thread: 1
MyThread: 1
...

 

위와 같이 메인 스레드와 생성한 스레드가 병렬적으로 실행되는 것을 확인할 수 있습니다.

 

(표준 입출력)

- 표준 입출력이란,

프로그램에 입력하는 소스에 해당하는 키보드, 마우스, 파일 등의 정보를 받아들일 수 있는 방법과,

출력에 해당하는 모니터, 파일 등의 정보 표출 방법을 프로그래밍 언어 단계에서 제공해주는 기술을 의미합니다.

 

예를들어,

#include <stdio.h>

int main() {
    int age;

    // 입력 받기
    printf("나이를 입력하세요: ");
    scanf("%d", &age);  // 정수 입력 받기

    // 출력하기
    printf("당신의 나이는 %d살입니다.\n", age);

    return 0;
}

 

위와 같이 scanf 함수를 사용하여 키보드로 입력되는 입력값을 받아와 age 변수에 할당하고,

이를 printf 로 모니터에 출력할 수 있습니다.

 

(C 포인터 기본)

- C 언어에서 포인터(pointer)는 변수의 메모리 주소를 저장하는 변수입니다.

 

프로그램의 모든 변수는 물리적인 메모리상에 저장됩니다.

즉, 변수의 본질은 데이터가 메모리상 어느 위치에 어떻게 저장되어있느냐이고,

변수명은 그러한 메모리상의 주소 범위의 첫번째 값을 대변합니다.(데이터가 메모리 101번에서 110번까지를 점유하고 있다면 변수명은 101 에 접근하기 위한 별칭)

 

즉, 일반 변수는 이에 접근하면 바로 해당 메모리의 값을 반환하거나 값을 입력하는데에 사용되지만,

포인터 변수는 메모리상 주소를 저장하고, 다른 메모리 주소를 할당하는 데에 사용됩니다.

 

- 포인터와 관련된 C 언어 연산자는 2가지 종류가 있습니다.

& 연산자는, 일반 변수에 붙여 사용하면 해당 변수의 메모리 첫번째 주소를 얻어내는 역할을 하고,

* 연산자는 포인터 변수에 붙여 사용하면, 해당 주소 위치에 저장된 데이터 값에 접근하는 역할을 합니다.

 

- 간단한 예시는 아래와 같습니다.

#include <stdio.h>

int main() {
    int a = 10;
    int* p = &a;  // 변수 a의 주소를 포인터 p에 저장

    printf("a의 값: %d\n", a);          // 10
    printf("a의 주소: %p\n", &a);       // 예: 0x7ffee3f1c0ac
    printf("p의 값(저장된 주소): %p\n", p);     // a의 주소
    printf("p가 가리키는 값: %d\n", *p);        // 10

    *p = 20;  // 포인터를 통해 a 값을 변경
    printf("a의 값(변경 후): %d\n", a);  // 20

    return 0;
}

 

이에 대한 실행 예시는,

a의 값: 10
a의 주소: 0x7ffee3f1c0ac
p의 값(저장된 주소): 0x7ffee3f1c0ac
p가 가리키는 값: 10
a의 값(변경 후): 20

 

이러합니다.

 

- 이 이상의 자세한 설명은 생략합니다.

포인터는 C 언어의 꽃이고, C 언어를 어렵게 만드는 1등 공신이자 C 언어의 성능을 높이는 최고의 요소입니다.

이 요소로인해 디버깅이 어려운 위험한 에러를 만들어내기도 하므로 기본 개념에서부터 응용까지 익혀야 할 것이 많습니다.

C 언어와 성능 최적화, 알고리즘 등의 분야에 관심이 많으신 분이시라면 개인적으로 공부해보세요.

 

(Java 클래스, 상속, 캡슐화)

- Java 클래스는 위에서 설명했듯, 객체 지향 프로그래밍 개념을 Java 로 구현해낸 것입니다.

Java 는 코드 파일 하나에 클래스 파일 하나를 두는 방식으로 엄격하게 객체 지향 프로그래밍을 제약해둔 언어로,

이러한 엄격함과 논리적인 구조로 인하여 자바를 선호하는 개발자들이 많습니다.

 

- 자바 클래스는 아래와 같이 정의하고 사용합니다.

// 클래스 정의
public class Person {
    // 필드 (속성)
    String name;
    int age;

    // 생성자
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 메서드 (행동)
    public void introduce() {
        System.out.println("안녕하세요, 제 이름은 " + name + "이고, 나이는 " + age + "살입니다.");
    }
}

 

public class Main {
    public static void main(String[] args) {
        // Person 클래스의 객체 생성
        Person person1 = new Person("홍길동", 30);
        
        // 메서드 호출
        person1.introduce();
    }
}

 

- 객체지향 프로그래밍의 특징인 상속은, 객체를 나타내는 클래스나, 그 형태만을 의미하는 인터페이스를 상속하여 또다른 클래스를 만들 수 있다는 것입니다.

// 부모 클래스
class Animal {
    String name;

    public void sound() {
        System.out.println("동물이 소리를 냅니다.");
    }
}

// 자식 클래스
class Dog extends Animal {
    public void bark() {
        System.out.println("멍멍!");
    }
}

public class InheritanceExample {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.name = "바둑이";
        dog.sound();  // 부모 메서드
        dog.bark();   // 자식 메서드
    }
}

 

위와 같이 추상 개념의 상위 클래스를 보다 구체적인 하위 클래스에서 상속받아와 사용 가능합니다.

 

- 오버로딩이란, 같은 이름의 메서드를 매개변수 형태만 다르게 해서 여러 개 정의하는 것을 의미합니다.

class Calculator {
    public int add(int a, int b) {
        return a + b;
    }

    public double add(double a, double b) {
        return a + b;
    }

    public int add(int a, int b, int c) {
        return a + b + c;
    }
}

public class OverloadingExample {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        System.out.println(calc.add(3, 5));           // int
        System.out.println(calc.add(2.5, 4.3));       // double
        System.out.println(calc.add(1, 2, 3));        // int 3개
    }
}

 

위와 같이 같은 함수명이어도 매개변수가 다르다면 다른 함수로 구분됩니다.

 

- 오버라이딩이란, 부모 클래스의 메서드를 자식 클래스에서 재정의하는 것을 의미합니다.

메서드 시그니처(이름, 매개변수, 반환형)는 동일해야 합니다.

class Animal {
    public void sound() {
        System.out.println("동물이 소리를 냅니다.");
    }
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("야옹~");
    }
}

public class OverridingExample {
    public static void main(String[] args) {
        Animal animal = new Cat();
        animal.sound();  // Cat 클래스의 sound()가 호출됨
    }
}

 

위와 같이 Cat 이 상위 클래스의 sound 함수를 오버라이딩하면,

Cat 클래스를 이용하여 구현한 인스턴스는 '야옹~'이라고 오버라이딩 된 하위 클래스의 로직을 따릅니다.

저작자표시 비영리 변경금지 (새창열림)

'Study > Computer Science' 카테고리의 다른 글

[정보 처리] 18. GoF 디자인 패턴 총정리 (Java 예시 코드 수록)  (0) 2025.05.19
[정보 처리] 16. 물리 데이터베이스 설계  (0) 2025.05.14
[정보 처리] 15. DBMS 활용 (SQL, 인덱스, 뷰, 트랜젝션, 병렬 처리, DB Lock)  (1) 2025.05.14
[정보 처리] 14. 데이터베이스 기본, 설계, 정규화  (0) 2025.05.10
[정보 처리] 13. 소프트웨어 성능 분석, 품질 평가  (0) 2025.04.17
'Study/Computer Science' 카테고리의 다른 글
  • [정보 처리] 18. GoF 디자인 패턴 총정리 (Java 예시 코드 수록)
  • [정보 처리] 16. 물리 데이터베이스 설계
  • [정보 처리] 15. DBMS 활용 (SQL, 인덱스, 뷰, 트랜젝션, 병렬 처리, DB Lock)
  • [정보 처리] 14. 데이터베이스 기본, 설계, 정규화
Railly Linker
Railly Linker
IT 지식 정리 및 공유 블로그
  • Railly Linker
    Railly`s IT 정리노트
    Railly Linker
  • 전체
    오늘
    어제
  • 공지사항

    • 분류 전체보기 (106)
      • Programming (33)
        • BackEnd (18)
        • FrontEnd (2)
        • DBMS (1)
        • ETC (12)
      • Study (72)
        • Computer Science (20)
        • Data Science (17)
        • Computer Vision (16)
        • NLP (15)
        • ETC (4)
      • Error Note (1)
      • ETC (0)
  • 인기 글

  • 최근 글

  • 최근 댓글

  • 태그

    list
    localhost
    kotlin arraylist
    단축키
    kotlin linkedlist
    docker 배포
    jvm 메모리 누수
    MacOS
    springboot 배포
    Kotlin
    docker compose
    network_mode: "host"
    지리 정보
    unique
    kotlin mutablelist
    데이터베이스 제약
    논리적 삭제
  • 링크

    • RaillyLinker Github
  • hELLO· Designed By정상우.v4.10.0
Railly Linker
[정보 처리] 17. 데이터 전환(ETL), 프로그래밍 이론
상단으로

티스토리툴바