
언어는 기호 체계이며 의미 전달 수단이다.
언어라는 것은 사람이 소통을 위해 사용하는 언어와 컴퓨터 세계에서 사용하는 언어로 구분될 수 있는데요, 그 주체가 사람인지 시스템인지에 따라 나뉘긴 하지만 결국 중요한 것은 언어라는 것은 의미를 전달하고 해석하기 위한 기호 체계라는 것은 동일한 것 같습니다.
즉, 언어의 본질은 기호 + 문법 + 의미의 조합이고 컴퓨터 세상에서의 언어도 이 규칙을 지켜야 하는 것이죠
그렇다면 컴퓨터 세상에서의 언어는 정말 다양한 언어들이 존재하는데 이 언어들은 어떻게 구분될까요?
저는 언어의 사용방법에 따라 크게 명령형 언어와 선언형 언어로 구분할 수 있을 것 같습니다.
즉, html, css, js, python, java, c 등등은 서로 같은 언어가 아니라 구분을 할 수 있다는 것입니다.
HTML, CSS, XML와 같은 것들이 포함되는 언어입니다. 이 언어들은 표현만 하고 실행 로직은 없습니다. 즉, 사람이 시스템한테 명령을 내리는 것이 아니라 이미 의미와 실행 규칙이 정해진 구문들을 선언하여 구조를 잡거나 스타일을 입히는 방법으로 언어를 사용하는 것입니다.
선언형 언어도 목적에 따라 구조를 잡는 것을 목적으로 한 마크업 언어나 스타일을 목적으로 한 스타일 시트 언어로 구분할 수 있습니다.
반면에 명령형 언어는 개발자가 시스템에게 명령을 내릴 수 있도록 문법(Syntax)과 의미(Semantics)가 있고 실행 로직을 컴퓨터가 이해할 수 있는 언어로 변경할 수 있는 컴파일 또는 실행 체계를 포함한 언어를 의미합니다.
개발을 처음 시작했을 때 저는 'Java는 객체지향 언어다', 'Spring은 IoC와 DI를 제공한다'는 말을 그냥 활자 그대로 이해했는데요. 사실 처음에는 전혀 이해하지 못했다는 것입니다 ㅎㅎ
하지만 시간이 흐를수록 그 말들이 무슨 의미인지, 단순한 기술 용어 이상의 철학과 설계 원칙을 담고 있다는 것을 깨닫게 되었습니다.
이 글은 제가 객체지향과 설계 원칙, 그리고 스프링 프레임워크와의 연결을 이해해가는 과정을 아래에서 정리해보고자 합니다.
개발자들은 수십 년간 더 나은 개발을 위해 어떤 방식으로 프로그램을 설계하고 구현할지에 대해 치열하게 고민해왔을 것입니다. 그 결과 두 가지 주요 패러다임이 나왔고 다음과 같습니다:
코드가 위에서 아래로 순차적으로 실행되며, 데이터와 함수가 분리되어 있습니다.
대표 언어: C
현실 세계의 개념을 추상화하여 객체로 표현합니다
데이터(속성)와 함수(행위)를 객체 내부에 캡슐화합니다
객체들 간의 메시지 전달로 협력하여 기능을 구현하는 것이 포인트입니다
대표 언어: Java, Kotlin, Python, C++ 등
객체지향으로 개발한다고 해서 모두가 좋은 코드를 작성하는 것은 아닙니다. 저도 처음에 자바라는 언어를 배우고 뭣모르고 코드를 짜봤는데 어떻게 코드를 작성해야할지 암담했습니다. 그래서 어떻게 코드를 작성하는 것이 좋을까라는 것을 고민에서 찾아보니 객체지향 설계를 도와주는 설계 원칙과 패턴들이 있다는 것을 알게되었습니다.
객체지향 설계의 핵심 원칙들:
Single Responsibility Principle (단일 책임 원칙)
Open-Closed Principle (개방-폐쇄 원칙)
Liskov Substitution Principle (리스코프 치환 원칙)
Interface Segregation Principle (인터페이스 분리 원칙)
Dependency Inversion Principle (의존성 역전 원칙)
객체지향 원칙들을 기반으로, 반복되는 문제를 해결하기 위한 검증된 설계 방식
예: Singleton, Factory, Proxy, Strategy, Observer 등
이 둘은 서로 유기적으로 연결됩니다
즉, 디자인 패턴들은 SOLID 원칙을 실천하는 구체적인 도구이며 SOLID 원칙은 OOP 설계를 잘 수행하기 위한 추상적인 기준이라고 할수 있습니다.
처음에 Spring은 DI(의존성 주입)과 IoC(제어의 역전)을 제공한다는 설명만 들었을 땐 정확히 와닿지 않았습니다. 하지만 아래 개념들을 이해하면서 그 의미가 명확해졌습니다.
제어권을 개발자가 아닌 프레임워크가 갖는 구조
개발자는 객체를 직접 생성하지 않고 IoC Container(Spring Container)에게 객체 생성을 맡김
라이브러리는 개발자가 호출하지만 프레임워크는 개발자의 코드를 호출함
Spring에서는 이를 ApplicationContext라는 IoC Container가 구현하고 있습니다.
즉, IoC Container는 객체를 새로 만들어주는 존재가 아니라 개발자가 제공한 정보(POJO + 설정 정보)를 기반으로 생명주기를 관리해주는 자동화 시스템인 것입니다.
POJO: 순수한 자바 객체 (비즈니스 로직을 담은 클래스)
Configuration Metadata: 객체를 어떻게 생성하고 연결할지를 기술한 정보 (예: @Configuration, @Bean, XML 등)
Spring은 POJO를 빈으로 등록하고, DI를 통해 객체 간의 결합도를 낮추고, 유연하게 변경이 가능한 구조를 제공합니다.
의존성이 필요한 객체를 내부에서 new로 생성하지 않고 외부에서 주입 받는 방식
주입을 통해 클래스는 자신이 사용하는 의존 객체의 구현에 대해 알 필요가 없고 인터페이스에만 의존할 수 있습니다
이는 곧 추상화에 의존 → OCP, DIP 실현으로 이어지게 됩니다.
결국, 우리가 사용하는 스프링 프레임워크는 OOP 철학을 실천할 수 있도록 돕는 도구이며, 이를 위해 내부적으로 IoC, DI, 프록시, AOP 등의 메커니즘을 활용한다고 정리할 수 있을 것 같습니다.
따라서 저는 다음과 같은 순서를 따라가며 각각의 항목에 대 더욱 깊은 이해로 나아가고자 합니다.
프로그래밍 언어와 그 문법/의미 구조에 대한 이해
OOP의 본질: 추상화, 캡슐화, 메시지 전달, 설계의 유연함
설계 원칙(SOLID)과 디자인 패턴의 필요성
이러한 철학이 구현된 프레임워크(Spring)의 내부 동작
공부를 하다 갑자기 언어란 뭐지? 객체지향 프로그래밍이란 뭐지? 디자인 패턴은? SOLID는? 그렇다면 Spring은? 이라는 생각이 들면서 그러한 모든 일련의 과정들을 제 나름대로 정리해보고자 이번 글을 작성한 것입니다.
아직 이해하지 못한 것도 많고 특정 개념에 대한 설명을 요구하면 정리해서 말을 하지 못하는 경우가 많은데 점점더 이거는 이거야라고 분명하게 얘기할 수 있는 것들이 많아지는 저를 보며 개발자로서 성장하고 있다는 생각이 듭니다.
그럼 다음에는 더 의미있고 깊이있는 주제로 찾아오겠습니다. 긴 글 읽어주셔서 정말 감사드립니다.