이번 글에서는 데이터 사이언스를 처음 접하는 개발자의 관점에서 데이터 사이언스가 무엇인지, 왜 중요해졌는지, 어떤 과정을 거치는지 차근차근 정리해본다.
Python으로 데이터 분석이나 머신러닝을 시작할 때 가장 먼저 부딪히는 문제 중 하나가 개발 환경 구성이다.
데이터 분석을 시작하면 가장 자주 하는 작업 중 하나가 필요한 데이터만 골라내는 것이다. 전체 데이터에서 특정 행이나 열을 선택하고, 조건에 맞는 데이터만 추출하거나, 배열의 모양을 바꾸는 작업은 전처리와 분석의 기본이 된다.
데이터 분석을 하다 보면 단순히 데이터를 조회하는 것보다 새로운 열을 만들고, 기존 값을 수정하고, 필요 없는 행이나 열을 제거하고, 외부 API 데이터를 DataFrame으로 변환하는 작업을 훨씬 자주 하게 된다.
데이터 분석에서는 여러 데이터셋을 하나로 합치는 작업이 자주 발생한다. 회원 정보와 주문 정보처럼 공통 Key를 기준으로 관계를 맺어 합치는 경우도 있고, 월별 로그처럼 같은 구조의 데이터를 위아래로 이어 붙이는 경우도 있다.
데이터 분석을 하다 보면 전체 데이터의 평균 하나보다 카테고리별, 사용자 유형별, 기간별 특징이 더 중요한 경우가 많다. 예를 들어 쇼핑몰 데이터가 있다면 전체 매출만 보는 것보다 다음 질문이 더 실용적이다.
서비스 데이터에는 거의 항상 시간이 붙는다. 날짜별 가입자 수, 시간대별 API 요청 수, 일별 주문 금액, 서버 자원 사용률처럼 시간의 순서에 따라 값이 변하는 데이터를 다루려면 일반적인 표 조작만으로는 부족하다.
데이터 분석을 시작하면 생각보다 자주 마주치는 것이 결측치(Missing Value) 다. 회원 가입 폼에서 선택 입력값이 비어 있을 수도 있고, API 응답 일부가 누락될 수도 있고, 로그 수집 중 오류가 발생해 특정 구간의 값이 저장되지 않을 수도 있다.
데이터를 분석하다 보면 대부분의 값과 동떨어진 이상치(Outlier) 를 만나게 된다.
데이터 전처리에서 중요한 작업 중 하나가 변수의 표현 방식을 분석 목적에 맞게 바꾸는 것이다. 원본 데이터가 그대로도 의미가 있을 수 있지만, 분석이나 머신러닝에 바로 사용하기에는 지나치게 세밀하거나 변수마다 값의 범위가 너무 다를 때가 있다.
데이터 분석이나 머신러닝을 시작하면 처음에는 원본 데이터를 깨끗하게 만드는 것에 집중하게 된다. 결측치를 처리하고, 이상치를 확인하고, 스케일을 맞추는 식이다. 그런데 전처리는 단순히 데이터를 정리하는 데서 끝나지 않는다.
데이터 전처리를 공부하다 보면 처음에는 결측치 처리, 이상치 제거, 인코딩, 스케일링처럼 데이터의 형태를 바꾸는 작업에 집중하게 된다. 그런데 실제 머신러닝에서는 입력 변수의 개수가 많아질수록 또 다른 문제가 생긴다.
데이터 분석을 시작할 때 바로 머신러닝 모델을 만들거나 복잡한 시각화를 그리는 것보다 먼저 해야 할 일이 있다. 바로 데이터가 어떤 모습인지 확인하는 것이다.
데이터를 숫자로만 확인하면 전체적인 특징을 빠르게 파악하기 어려울 때가 있다. 평균, 중앙값, 빈도, 사분위수 같은 통계량은 중요하지만 분포의 모양이나 범주 간 차이를 직관적으로 이해하려면 시각화가 훨씬 효과적이다.
데이터를 분석할 때 하나의 변수만 따로 보는 것만으로는 충분하지 않은 경우가 많다. 예를 들어 쇼핑몰 데이터에 다음과 같은 컬럼이 있다고 해보자. 각 컬럼의 평균이나 빈도만 보는 것은 일변량 분석이다.
데이터 탐색에서 하나의 변수만 보면 분포는 알 수 있지만, 변수끼리 어떤 관계를 갖는지는 확인하기 어렵다. 실제 분석에서는 다음과 같은 질문이 더 중요할 때가 많다.
데이터를 비교하다 보면 단순히 평균값이 다르다는 사실보다 더 중요한 질문이 생긴다. 이 차이가 우연히 발생한 것인지, 실제로 의미 있는 차이인지 어떻게 판단할까?
데이터를 보다 보면 두 변수가 같이 움직이는지 궁금해지는 순간이 많다. 예를 들어 웹 서비스라면 이런 질문을 생각할 수 있다. 페이지 체류 시간이 길수록 클릭 횟수도 늘어나는가? API 응답 시간이 길수록 이탈률이 높아지는가?
데이터 분석을 하다 보면 단순히 변수 사이의 관계를 확인하는 것에서 한 걸음 더 나아가, 어떤 변수가 결과값에 얼마나 영향을 주는지 설명하거나 새로운 값을 예측하고 싶은 상황이 생긴다.
웹 서비스의 일별 방문자 수, 쇼핑몰의 월별 주문량, 서버의 시간별 요청 수, 금융 데이터의 일별 가격처럼 시간 순서가 중요한 데이터가 있다. 이런 데이터는 일반적인 표 형태의 데이터와 다르게, 과거 값이 현재 값에 영향을 줄 수 있다는 점이 핵심이다.
데이터 분석을 하다 보면 평균, 표준편차, 상관계수처럼 숫자로 요약한 결과만으로는 데이터의 모습을 충분히 파악하기 어려운 경우가 많다. 그래서 분석 초반에는 통계값과 시각화를 함께 확인하는 습관이 중요하다.
데이터 분석을 하다 보면 단순히 숫자를 출력하는 것만으로는 데이터의 흐름을 파악하기 어렵다. 특히 시간에 따른 변화, 두 지표의 비교, 여러 그래프를 한 화면에 배치하는 작업은 시각화가 훨씬 직관적이다.
데이터 분석에서 시각화는 단순히 결과를 예쁘게 보여주는 작업이 아니다. 숫자만 봤을 때 놓치기 쉬운 변수 간 관계, 그룹별 차이, 분포, 이상 패턴을 빠르게 발견하는 데 도움을 준다.
데이터 시각화는 숫자를 그래프로 바꾸는 것에서 끝나지 않는다. 사용자가 그래프 위에 마우스를 올려 값을 확인하고, 특정 구간을 확대하거나, 범례를 눌러 필요한 데이터만 골라 볼 수 있다면 같은 데이터에서도 훨씬 많은 정보를 전달할 수 있다.
주식 데이터를 분석할 때 숫자 테이블만 보면 시가(Open), 고가(High), 저가(Low), 종가(Close)의 관계나 거래량 변화를 한눈에 파악하기 어렵다.
Python으로 데이터 분석을 하다 보면 결과를 노트북 안에서만 확인하는 데 한계가 생긴다. 분석 결과를 다른 사람에게 보여주거나, 사용자가 직접 옵션을 바꿔가며 결과를 확인하게 하려면 웹 UI가 필요하다.
Python을 처음 시작할 때는 문법을 많이 외우기보다 코드가 어디에서 실행되고, 데이터를 어떤 형태로 다루며, 필요한 기능을 어떻게 찾아 사용하는지를 이해하는 것이 중요하다.
프로그램은 기본적으로 작성된 코드를 위에서 아래로 순서대로 실행한다. 하지만 실제 프로그램에서는 조건에 따라 특정 코드를 실행하거나 건너뛰고, 여러 데이터를 대상으로 **같은 작업을 반복해서 실행하는 흐름**이 필요하다.
Python을 처음 공부할 때는 문법을 조각처럼 외우기보다 코드가 어떻게 실행되고, 데이터가 어떤 형태로 저장되며, 조건과 반복으로 흐름을 어떻게 제어하는지를 연결해서 이해하는 것이 중요하다.
Python을 처음 공부할 때 가장 먼저 익혀야 하는 것은 문법 자체보다 값을 저장하고, 계산하고, 조건에 따라 실행 흐름을 바꾸는 방법이다.
Python을 조금만 공부하다 보면 같은 코드를 여러 번 작성하게 된다. 처음에는 복사해서 붙여넣어도 되지만 코드가 커질수록 수정해야 할 장소가 늘어나고, 로직의 의도도 파악하기 어려워진다. 이 문제를 해결하는 핵심 도구가 함수, 모듈, 패키지다.
프로그램에서는 값을 하나씩 저장하는 것보다 여러 데이터를 어떤 구조로 묶고 다룰지가 더 중요해지는 순간이 많다. 사용자 목록, 주문 내역, API 응답, 로그, 태그처럼 실제 데이터는 대부분 여러 값의 집합으로 표현된다.
Python에서는 숫자, 문자열, 리스트뿐 아니라 우리가 직접 정의한 데이터도 객체로 만들 수 있다. 프로그램이 커질수록 서로 관련된 데이터와 기능을 하나의 단위로 묶고 싶은 상황이 생긴다. 이때 사용하는 대표적인 방법이 객체지향 프로그래밍이다.
프로그램은 혼자 동작하지 않는다. 사용자에게 값을 입력받고, 파일을 읽고 저장하며, 예상하지 못한 상황에서는 오류를 처리해야 한다. 이 글에서는 Python 3 기준으로 다음 내용을 정리한다.
Python 문법을 한 번 훑고 나면 이런 고민이 생긴다. 문법은 알겠는데 실제 프로그램은 어떻게 만들어야 할까? 실제 개발에서는 문법을 많이 외우는 것보다 문제를 작게 나누고 적절한 도구를 조합하는 능력이 더 중요하다.
Python을 처음 배우면 가장 먼저 해야 할 일은 문법을 외우는 것이 아니라 Python이 어떤 언어인지, 그리고 내 컴퓨터에서 코드를 어떻게 실행하는지 이해하는 것이다.
Python 코드를 읽고 쓰기 위해서는 먼저 어떤 값을 어떤 자료형으로 표현하는지 이해해야 한다. 숫자를 계산할 때와 사용자 목록을 관리할 때, API 응답을 저장할 때 필요한 자료구조는 서로 다르다.
프로그램은 단순히 위에서 아래로 코드를 실행하는 것만으로는 부족하다. 상황에 따라 다른 작업을 실행하거나, 같은 작업을 여러 번 반복해야 한다. Python에서는 대표적으로 다음 세 가지 제어문을 사용한다.
코드가 길어질수록 같은 작업을 반복해서 작성하기보다 기능을 함수로 분리하고 입력과 출력을 명확하게 만드는 것이 중요하다. 이 글에서는 함수의 기본 구조부터 사용자 입력, 파일 처리, 명령행 인자까지 연결해서 정리한다.
Python 기초 문법을 익힌 뒤에는 코드를 어떻게 구조화할 것인지가 중요해진다.
Python을 사용하다 보면 비슷해 보이는 세 종류의 기능을 만나게 된다.
Python 기초 문법만으로도 프로그램을 만들 수 있지만 프레임워크나 라이브러리 코드를 읽다 보면 조금 더 추상적인 개념을 만나게 된다.
문자열에서 원하는 패턴을 찾을 때 단순한 in, startswith(), split()만으로 충분한 경우가 많다. 하지만 다음처럼 규칙이 있는 문자열을 다뤄야 한다면 정규표현식이 유용하다.
Python 문법을 한 번 공부한 뒤 가장 자주 생기는 고민은 다음과 같다. 문법은 이해했는데 프로그램은 어떻게 시작해야 할까?
Python을 공부하다 보면 단순히 문법을 모르는 것보다 내 코드가 왜 동작하지 않는지 설명하기 어려운 순간이 더 자주 온다. AI 도구는 이때 빠르게 원인을 찾고 개념을 다시 설명받는 데 유용하다.
Python으로 데이터 분석을 시작하면 문법보다 먼저 부딪히는 것이 실행 환경이다. Python은 설치되어 있는데 numpy가 없다고 나오거나, 터미널에서는 설치했는데 Jupyter에서는 import가 안 되거나, 프로젝트마다 패키지 버전이 달라 충돌하는 일이 흔하다.
NumPy와 Pandas를 바로 시작할 수도 있지만 Python의 기본 문법을 모르면 데이터 분석 코드가 왜 동작하는지 이해하기 어렵다. 특히 다음 개념은 데이터 분석 코드에서 계속 반복해서 등장한다.
Python의 리스트만으로도 숫자 데이터를 처리할 수 있지만, 데이터가 많아질수록 반복문 중심의 계산은 코드가 길어지고 속도도 느려질 수 있다. NumPy는 이런 문제를 해결하기 위한 대표적인 수치 계산 라이브러리다.
NumPy가 수치 배열 계산에 강하다면 Pandas는 행과 열이 있는 표 형태 데이터를 다루는 데 강하다. CSV, Excel, 데이터베이스 조회 결과, API 응답처럼 실제 분석에서 만나는 데이터는 대부분 표 형태로 정리할 수 있다.
실제 데이터 분석에서는 단순히 행을 필터링하는 것보다 범주별로 묶고, 요약하고, 시간 단위로 재구성하는 작업이 훨씬 자주 등장한다.
데이터를 표로만 보면 전체 패턴을 빠르게 파악하기 어렵다. 시각화를 사용하면 다음과 같은 질문에 더 쉽게 답할 수 있다.
알고리즘 트레이딩은 단순히 "주식을 자동으로 사고파는 코드"가 아니다. 실제로는 다음 요소가 연결된 데이터 처리 시스템에 가깝다.
주가 분석에서 가장 먼저 다루게 되는 데이터는 보통 OHLCV다.
전략을 만들었다면 바로 실제 주문에 연결하는 것이 아니라 과거 데이터로 전략이 어떻게 동작했는지 검증해야 한다. 이 과정을 백테스트라고 한다. 하지만 백테스트는 단순히 과거 수익률을 계산하는 작업이 아니다.
증권 API를 사용하면 Python 코드에서 시장 데이터를 조회하거나 주문을 전송할 수 있다. 하지만 실제 자동매매 시스템을 만들 때는 단순히 requests.get() 한 번 호출하는 것보다 다음 구조를 이해하는 것이 중요하다.
주가 데이터를 매번 API에서 다시 받아오면 느리고 호출 제한에도 걸릴 수 있다. 따라서 알고리즘 트레이딩이나 데이터 분석 프로젝트에서는 과거 데이터를 로컬 데이터베이스에 저장해두고 재사용하는 구조가 유용하다.
트레이딩 프로그램에 GUI가 반드시 필요한 것은 아니다. 백그라운드 자동매매나 종목 스크리너는 CLI 프로그램으로도 충분히 만들 수 있다. 하지만 다음 기능이 필요하다면 데스크톱 GUI가 유용할 수 있다.
알고리즘 트레이딩에서 모든 종목을 사람이 직접 확인하는 것은 비효율적이다. 그래서 특정 조건을 만족하는 종목만 자동으로 추려내는 스크리너(Screening) 를 만들 수 있다.
Java를 처음 시작하면 코드보다 먼저 JDK, JVM, javac, .class 같은 용어를 만나게 된다. 처음에는 전부 외워야 할 것처럼 보이지만, 실제로는 Java 코드가 어떤 과정을 거쳐 실행되는지 한 번 이해하면 대부분 자연스럽게 연결된다.
프로그램은 결국 데이터를 저장하고 바꾸고 읽는 일을 반복한다. 로그인한 사용자 수, 상품 가격, 결제 여부, API 응답 메시지처럼 프로그램이 다루는 값은 계속 변한다. Java에서는 이런 값을 변수(variable) 에 저장한다.
변수에 값을 저장했다면 다음 단계는 그 값을 계산하고 비교하는 것이다. Java의 연산자는 숫자 계산뿐 아니라 조건식 작성, 문자열 연결, 값 변경 등 거의 모든 코드에서 사용된다.
프로그램은 상황에 따라 다른 동작을 해야 한다. 로그인 상태에 따라 화면을 바꾸고, 주문 상태에 따라 버튼을 다르게 보여주고, 권한에 따라 기능 접근을 제한한다. Java에서는 이런 분기 처리를 조건문으로 구현한다.
같은 코드를 여러 번 실행해야 할 때 직접 복사해서 작성하면 코드가 길어지고 수정도 어려워진다. 반복문은 이런 반복 작업을 하나의 구조로 표현한다. 출력:while은 조건식이 true인 동안 블록을 반복한다.
Java 코드를 작성하다 보면 "이 변수는 왜 여기서 사용할 수 없지?", "왜 int는 double에 들어가는데 반대는 안 되지?" 같은 질문을 만나게 된다. 이 두 문제는 각각 스코프(scope) 와 형변환(type casting) 을 이해하면 해결된다.
지금까지 변수에 값을 직접 작성했다면 이제는 사용자가 입력한 데이터를 프로그램에서 받아볼 차례다. 콘솔 기반 Java 프로그램에서는 Scanner를 사용해 입력을 받을 수 있다. 먼저 클래스를 import한다. 그리고 입력을 읽을 Scanner 객체를 만든다.
사용자 점수 3개를 저장한다면 변수 세 개로도 가능하다. 하지만 데이터가 100개라면 변수 100개를 만들 수는 없다. 같은 타입의 데이터를 여러 개 관리할 때 사용하는 구조가 배열(array) 이다.
프로그램이 커질수록 같은 코드가 반복되고 하나의 main() 메서드가 길어지기 쉽다. 이 문제를 해결하는 핵심 도구가 메서드(method) 다.메서드는 특정 작업을 이름 붙인 코드 블록으로 분리하고 필요할 때 호출할 수 있게 해준다.
Java의 변수, 연산자, 조건문, 반복문을 각각 이해했다고 해서 바로 프로그램을 만들 수 있는 것은 아니다. 실제 코드는 여러 문법을 동시에 사용해 하나의 문제를 해결한다.
Java를 처음 시작하면 문법보다 먼저 헷갈리는 것이 JDK가 무엇인지, int와 String은 왜 다른지, 값을 변수에 어떻게 담는지 같은 기본 개념이다.
변수에 값을 저장할 수 있게 되면 다음 단계는 그 값을 계산하고, 비교하고, 조건에 따라 다른 코드를 실행하고, 필요한 만큼 반복하는 것이다.
프로그램이 조금만 커져도 변수를 하나씩 만들고 같은 코드를 반복해서 작성하는 방식은 금방 한계가 온다. Java에서는 배열로 같은 타입의 데이터를 묶고, 메소드로 반복되는 동작을 하나의 기능 단위로 나눌 수 있다.
Java를 제대로 이해하려면 문법보다 객체지향 프로그래밍(OOP) 을 이해하는 것이 중요하다. 객체지향의 핵심은 데이터를 따로 두고 함수를 따로 두는 것이 아니라, 서로 관련 있는 상태와 행동을 하나의 객체로 묶어 프로그램을 구성하는 것이다.
Java 기초 문법과 객체지향을 익혔다면 다음으로 자주 만나는 개념이 제네릭, 컬렉션, 람다, 스트림이다. 이 기능들은 데이터를 타입 안전하게 보관하고, 반복되는 코드를 줄이며, 컬렉션을 선언적으로 처리하는 데 사용된다.
문법만 알고 프로그램을 만들면 곧 세 가지 현실적인 문제를 만나게 된다. 예상하지 못한 입력이나 오류가 발생한다.여러 작업을 동시에 처리해야 한다. 콘솔이나 파일처럼 프로그램 외부와 데이터를 주고받아야 한다.
Java를 시작할 때 가장 먼저 헷갈리는 것은 코드를 작성하는 일보다 설치와 실행이다. java와 javac는 무엇이 다르고, JDK를 설치했는데 왜 실행이 안 되는 걸까? 환경 구성과 실행 원리를 한 번에 정리해 보자.
Java는 변수마다 어떤 형태의 데이터를 보관할지 명시하는 언어다. 이 제약 덕분에 개발자는 값을 사용하기 전에 잘못된 타입 조합을 확인할 수 있다. 가장 기본적인 자료형부터 연산 중 발생하는 의외의 결과까지 살펴보자.
회원의 주문 내역에서 특정 금액 이상의 주문만 골라내려면 무엇이 필요할까? 주문 금액을 저장할 배열, 각 주문을 훑어볼 반복문, 조건에 따라 결과를 분리할 조건문이 필요하다. 세 가지를 따로 외우기보다 하나의 문제로 연결해 보자.
개발 초기에는 main()에 모든 코드를 넣기 쉽다. 그런데 기능이 조금만 늘어나도 같은 계산을 반복하고, 수정할 위치를 찾기 어려워진다. 이럴 때 메서드(method)로 하나의 역할을 분리한다.
함수로 로직을 분리했다고 프로그램 구조가 완성되는 것은 아니다. 서로 관련된 데이터와 기능이 여러 곳에 흩어지면 관리가 어려워진다. Java의 객체지향 프로그래밍(OOP)은 관련 상태와 동작을 하나의 객체로 묶을 수 있게 한다.
같은 기능을 구현한 클래스가 여러 개 있을 때, 호출하는 코드까지 모두 따로 만들어야 할까? 다형성(polymorphism)을 이용하면 구현체가 달라도 공통 계약에 따라 다룰 수 있다. 여기서는 결제 수단을 바꿔 끼울 수 있는 구조로 핵심 문법을 이해해 보자.
Java 파일이 하나일 때는 컴파일과 실행이 어렵지 않다. 하지만 클래스가 여러 개이고, 패키지 폴더까지 나뉘면 ClassNotFoundException이나 NoClassDefFoundError가 등장하기 시작한다.
프로그램은 언제든 실패할 수 있다. 사용자 입력이 잘못될 수도 있고, 파일이 없거나 외부 서비스가 응답하지 않을 수도 있다. 예외 처리(exception handling)는 오류를 무조건 숨기는 기술이 아니라 실패를 적절한 계층에서 설명하고 대응하기 위한 방법이다.
Java의 배열은 편리하지만 크기를 한번 정하면 늘리기 어렵다. 사용자 목록, 장바구니, 주문 상태처럼 데이터가 동적으로 바뀌는 프로그램에는 보다 유연한 도구가 필요하다. 이때 컬렉션 프레임워크와 제네릭을 함께 사용한다.
Java 문법으로 작은 프로그램을 만들 때는 main()에서 객체를 직접 연결해도 충분하다. 하지만 웹 서비스에는 HTTP 요청 처리, 객체 간 연결, DB 트랜잭션, 설정 등 반복되는 기반 작업이 많다.
의존성 주입은 단순히 @Autowired를 사용하는 기술이 아니다. 서비스가 필요한 도구를 직접 만들지 않고, 외부에서 전달받도록 설계하는 것이다. 이 구조는 구현체 변경뿐 아니라 테스트의 난이도까지 바꾼다.
작은 Java 프로그램에서는 new로 필요한 객체를 만들어도 충분하다. 문제는 서비스가 커졌을 때 객체의 생성·연결·교체 책임이 여러 클래스에 흩어지는 것이다.
여러 서비스 메서드마다 실행 시간을 기록해야 한다면 System.nanoTime()을 복사해 넣는 방법이 떠오를 수 있다. 하지만 로그 정책이 바뀔 때 모든 메서드를 수정해야 한다.
주문 정보를 DB에 저장한 뒤 재고를 감소시키는 프로그램을 생각해보자. 주문 저장은 성공했는데 재고 변경에 실패하면 데이터가 모순될 수 있다. 이런 여러 변경을 성공하면 함께 확정하고 실패하면 함께 취소하도록 묶는 단위가 트랜잭션(Transaction)이다.
프로그램을 만든 뒤 데이터 저장소를 바꾸거나 캐시 구현체를 교체해야 할 때가 있다. 이때 사용처마다 특정 구현 API를 직접 호출하고 있다면 수정할 곳이 많아진다.
자바로 만든 웹 서비스가 브라우저의 요청을 받으려면 무엇이 필요할까? Java 코드만 작성하면 끝나는 게 아니다. 코드를 컴파일하고, 필요한 라이브러리를 연결하고, HTTP 요청을 받을 서버를 실행해야 한다.
프런트엔드에서 fetch('/products')를 호출했을 때 백엔드에서는 무슨 일이 일어날까? Spring MVC의 핵심은 HTTP 요청을 적절한 메서드에 연결하고, 입력값을 자바 객체로 변환한 다음, HTML 또는 JSON 응답을 만드는 것이다.
프로젝트 규모가 작을 때는 필요한 객체를 new로 만들면 된다. 하지만 서비스가 커지면 객체를 누가 생성하고, 누구에게 전달하고, 언제 폐기할지를 일관되게 관리하기 어려워진다.
의존성 주입(Dependency Injection, DI)은 클래스가 필요한 객체를 직접 만들지 않고 외부에서 받는 방식이다. 언뜻 코드 한두 줄 줄이는 기능처럼 보이지만, 실제 가치는 구현을 교체하고 테스트하기 쉬워진다는 데 있다.
여러 서비스 메서드의 실행 시간을 측정하려고 한다고 가정해 보자. 모든 메서드에 시간을 기록하는 코드를 넣으면 기능은 동작하겠지만, 동일한 코드가 여러 곳에 흩어지고 비즈니스 로직을 읽기 어려워진다.
백엔드 서비스를 개발하다 보면 데이터베이스 트랜잭션, 캐시 저장소, HTTP 서버 같은 기술을 사용하게 된다. 처음부터 특정 구현체의 세부 API에 모든 업무 코드를 맞추면, 기술을 변경할 때 수정 범위가 커질 수 있다.
자바를 처음 실행할 때 헷갈리는 것은 문법보다 코드를 작성한 파일이 어떤 과정을 거쳐 실행되는지다. 이 흐름을 이해하면 IDE에서 실행이 실패했을 때도 어디를 확인해야 할지 알 수 있다.
웹 서비스에서 주문 수량, 결제 금액, 사용자 이름은 서로 성격이 다른 데이터다. 자바는 데이터를 다루기 전에 어떤 타입인지 지정한다. 초반에는 불편해 보여도 타입 덕분에 잘못된 연산을 컴파일 단계에서 많이 잡을 수 있다.
실제 서비스 데이터는 하나의 값보다 여러 개의 값과 그 관계로 이루어질 때가 많다. 유저의 관심 상품 목록, 상품 ID별 재고 수량, 중복 없는 방문자 ID는 각각 다른 자료구조가 잘 맞는다.
서비스 로직을 작성하다 보면 "재고가 있으면 결제 진행", "여러 주문을 순회하면서 합계 계산" 같은 요구가 반복된다. 이때 사용하는 도구가 조건문과 반복문이다. 문법 자체보다 중요한 것은 어떤 상황에서 어떤 구문을 고를지 판단하는 일이다.
자바의 객체지향을 이해하려면 class 문법부터 외우는 것보다 관련된 데이터와 동작을 한곳에 묶는 이유를 생각해 보는 것이 좋다. 예를 들어 회원의 포인트를 변경하는 작업이 프로그램 곳곳에 흩어져 있다면 정책이 바뀔 때마다 여러 파일을 고쳐야 한다.
사용자에게 알림을 발송하는 기능이 있다고 가정해 보자. 처음에는 이메일만 사용하지만 나중에는 SMS나 푸시 알림이 추가될 수 있다. 발송 방식마다 분기문을 늘리는 대신 공통 규약을 정의하고 구현을 교체할 수 있게 만들면 코드가 단순해진다.
자바 프로그램이 조금만 커져도 외부 데이터와 상호작용해야 한다. 사용자가 입력한 숫자를 읽고, 파일에 결과를 저장하고, 잘못된 입력이 들어왔을 때 안전하게 실패해야 한다. 이 과정을 이해하려면 입출력과 예외 처리를 함께 보는 것이 좋다.
웹 서비스에서는 요청 처리, 로그 수집, 데이터 가공처럼 여러 작업을 다룬다. 자바는 스레드로 작업을 병행하고, 람다와 Stream으로 데이터 처리 로직을 간결하게 표현할 수 있다.
기본문법을 안다고 바로 프로그램을 설계할 수 있는 것은 아니다. 실제 요구사항은 "어떤 입력을 받아 무엇을 반환해야 하는가"부터 결정해야 한다. 이번에는 주문 데이터를 요약하고 페이지 단위로 조회하는 작은 기능을 작성하면서 문제를 코드로 바꾸는 과정을 연습해 보자.
개발을 하다 보면 final.txt, final\_진짜.txt, final\_진짜최종.txt처럼 파일을 복사해서 이전 상태를 남기고 싶을 때가 있다.
Git을 쓰는 가장 큰 이유 중 하나는 실패할 수 있는 작업을 안전하게 분리할 수 있다는 것이다. 새 기능을 만들거나 실험적인 코드를 수정할 때 바로 main에 작업하면 기존의 안정적인 상태까지 영향을 받을 수 있다.
로컬 Git만으로도 버전 관리는 가능하지만 여러 컴퓨터에서 작업하거나 다른 사람과 협업하려면 원격 저장소(remote repository)가 필요하다. GitHub는 로컬 Git 저장소를 인터넷에 연결해 백업, 공유, 코드 리뷰, 이슈 관리까지 할 수 있게 해준다.
Python으로 데이터를 다루기 시작하면 문법을 하나씩 외우는 것보다 데이터가 코드 안에서 어떻게 저장되고, 묶이고, 선택되고, 반복 처리되는지 흐름을 이해하는 것이 중요하다.
Python으로 표 형태의 데이터를 다룰 때 가장 자주 사용하는 도구가 Pandas DataFrame이다. DataFrame을 익힐 때는 메서드를 하나씩 외우기보다 실제 데이터 처리 순서로 연결해서 보는 편이 이해하기 쉽다.
데이터 분석을 시작하면 숫자부터 잔뜩 보게 된다. 평균, 최댓값, 표준편차 같은 통계량도 중요하지만, 숫자만으로는 데이터가 어떤 방향으로 움직이는지 한눈에 파악하기 어렵다.그래서 데이터 분석에서는 보통 수치화와 시각화를 함께 사용한다.
데이터 분석을 처음 접하면 그래프나 머신러닝 모델부터 떠올리기 쉽다. 하지만 실제 분석에서 더 먼저 필요한 것은 무엇을 해결하려는지 정확하게 정의하는 것이다. 데이터가 아무리 많고 복잡한 모델을 사용하더라도 질문이 잘못되면 분석 결과는 쓸모가 없을 수 있다.
데이터프레임을 처음 열었을 때 바로 변수끼리의 관계부터 비교하기보다, 먼저 각 변수 하나가 어떤 특징을 가지고 있는지 확인하는 것이 좋다. 이처럼 하나의 변수를 중심으로 분석하는 것을 단변량 분석(Univariate Analysis)이라고 한다.
데이터 분석에서 자주 막히는 지점은 "어떤 그래프를 그리고, 어떤 통계 검정을 써야 하지?"라는 질문이다.
데이터를 분석하다 보면 자연스럽게 다음 질문이 생긴다. 지금까지의 사용 기록을 보고, 새로운 사용자가 결제를 완료할지 예측할 수 있을까? 과거 데이터의 패턴을 이용해 새로운 데이터에 대한 판단을 내리는 것이 머신러닝(Machine Learning)의 대표적인 역할이다.
웹 서비스의 다음 주 주문 금액을 예측하는 모델을 만들었다고 하자. 예측 결과가 53만 원이고 실제 금액이 50만 원이었다면 예측은 3만 원 빗나갔다. 하지만 평가 대상이 수백 건이라면 오차를 하나씩 나열해서는 모델의 품질을 판단하기 어렵다.
스팸 메일 탐지기나 결제 이상 거래 탐지기는 결과가 둘 중 하나로 나뉘는 분류 모델이다. 그런데 "100개 중 95개를 맞혔다"는 사실만으로 좋은 모델이라고 말할 수 있을까? 위험한 거래가 100건 중 2건뿐이라면 모든 거래를 정상으로 예측해도 정확도는 98%가 나온다
웹 서비스의 처리 시간이 무엇에 따라 달라지는지 알고 싶다고 하자. 요청 크기가 커질수록 시간이 오래 걸릴까? 동시에 접속한 사용자 수가 늘어나면 얼마나 느려질까?
새로운 사용자에게 가장 비슷한 기존 사용자들을 찾아 관심 있을 만한 콘텐츠를 추천한다고 생각해 보자. 여기서 가장 기본적인 질문은 누가 얼마나 가까운 이웃인가?이다.
"사용자가 결제 버튼을 눌렀는가?", "최근 오류가 있었는가?", "재시도 횟수가 기준 이상인가?"처럼 조건문을 따라 결과를 판단하는 방식은 개발자에게 익숙하다.결정트리(Decision Tree)는 이런 질문을 여러 단계로 나누어 데이터를 구분하는 머신러닝 알고리즘이다
Logistic Regression이라는 이름을 처음 보면 회귀 모델이라고 생각하기 쉽다. 하지만 Scikit-learn에서 LogisticRegression은 분류를 위한 알고리즘이다.
한 사람의 판단보다는 여러 사람의 판단을 모으면 더 안정적인 결론을 얻을 때가 있다. 머신러닝에서도 하나의 모델만 사용하는 대신 여러 모델의 예측을 결합하는 방법을 사용한다.이 접근을 앙상블(Ensemble)이라고 한다. 대표적인 기법인 Bagging과 Boosting
머신러닝 모델을 만들었다고 끝은 아니다. KNN은 이웃을 몇 개 살펴볼지, 결정트리는 얼마나 깊게 성장시킬지, 랜덤 포레스트는 트리를 몇 개 만들지 선택해야 한다. 이처럼 학습 전에 개발자가 설정하는 값을 하이퍼파라미터(Hyperparameter)라고 한다.
모델에 데이터를 넣고 fit()을 호출하면 어느 순간 예측이 가능해진다. 그런데 모델 내부에서는 정확히 어떤 일이 일어나는 걸까? 딥러닝을 제대로 활용하려면 복잡한 수식을 모두 외우기보다 예측 → 오차 계산 → 가중치 수정이라는 흐름을 이해하는 것이 먼저다.
딥러닝으로 숫자를 예측하는 가장 기본적인 작업은 회귀(Regression) 다. 배송 시간, 서비스 이용량, 주문 금액처럼 연속된 수치를 예측할 때 사용한다. 이번에는 가상의 쇼핑몰 행동 데이터로 시작해 전처리부터 신경망 학습, 성능 평가까지 직접 연결해 본다.
고객이 구독을 해지할지, 결제가 실패할지, 사용자가 알림을 클릭할지처럼 결과가 두 가지인 문제를 이진 분류(Binary Classification) 라고 한다. 이진 분류의 핵심은 회귀와 달리 결과를 확률로 표현하고, 그 확률을 기준에 따라 클래스에 할당한다는 점이다.
회원 상태가 '일반/휴면/탈퇴' 중 하나이거나 고객 문의 유형이 여러 가지로 나뉜다면 다중 분류(Multiclass Classification) 가 필요하다. 이진 분류와 가장 다른 부분은 출력층의 노드 수, 활성화 함수, 정답 인코딩 방식에 따른 손실 함수다.
표 형태의 데이터뿐 아니라 이미지를 분류할 때도 신경망을 사용할 수 있다. 다만 이미지에는 높이·너비·채널이라는 추가적인 구조가 있다. 이번 글에서는 이미지를 픽셀 배열로 읽고, Dense 신경망으로 분류하고, 잘못 예측한 사례를 살펴보는 과정을 실습한다.
학습 정확도가 높아질수록 좋은 모델일까? 항상 그렇지는 않다. 학습 데이터에서는 99%를 맞히지만 처음 보는 사용자 데이터에서는 결과가 나빠지는 경우가 있다. 이것이 과적합(Overfitting) 의 대표적인 모습이다.
Sequential만으로도 많은 딥러닝 모델을 만들 수 있다. 하지만 서비스에서는 입력 데이터의 종류가 서로 다르거나 하나의 모델이 여러 결과를 동시에 예측해야 할 수 있다.
Keras로 모델을 한 번 만들어도 epochs, validation_split, 출력층 활성화 함수, 손실 함수 등을 섞어서 기억하기 쉽다. 아래 질문은 단순 암기보다 코드 속 옵션을 왜 그렇게 선택했는지 설명하는 연습에 초점을 맞췄다.