DI(Dependency Injection) - 스프링에서 객체 간의 의존관계를 개발자가 직접 관리하지 않고 외부(스프링 컨테이너)에서 주입해주는 패턴
| 구분 | 설명 |
|---|---|
| 의존성 | 하나의 객체가 다른 객체 없이는 제대로 된 역할을 할 수 없는 관계 |
| 주입 | 외부에서 객체를 생성해서 넣어주는 것 |
| IoC 컨테이너 | 스프링이 객체의 생성과 생명주기를 관리 |
// 1. 생성자 주입 (권장)
@Component
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
// 2. Setter 주입
@Component
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
// 3. 필드 주입 (비권장)
@Component
public class UserService {
@Autowired
private UserRepository userRepository;
}
싱글톤(Singleton) - 클래스의 인스턴스가 하나만 생성되도록 보장하는 디자인 패턴
| 특징 | 설명 |
|---|---|
| 기본 스코프 | 스프링 빈의 기본 스코프는 싱글톤 |
| 컨테이너 관리 | 스프링 컨테이너가 시작과 종료까지 관리 |
| 스레드 안전 | 스프링이 동시성 문제 해결 |
// 일반 싱글톤 패턴
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
// 스프링 싱글톤
@Component
public class SpringSingleton {
// 스프링 컨테이너가 자동으로 싱글톤 관리
}
| 스코프 | 설명 | 사용 시기 |
|---|---|---|
| prototype | 매번 새로운 인스턴스 생성 | 상태를 가져야 하는 빈 |
| request | HTTP 요청당 하나의 인스턴스 | 웹 애플리케이션에서 요청별 데이터 |
| session | HTTP 세션당 하나의 인스턴스 | 사용자별 세션 데이터 |
| application | ServletContext 생명주기와 동일 | 애플리케이션 전역 데이터 |
@Component
@Scope("prototype")
public class PrototypeBean {
private int count = 0;
public void addCount() {
count++;
}
public int getCount() {
return count;
}
}
// 사용 예시
@Component
public class ClientBean {
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.addCount();
return prototypeBean.getCount(); // 항상 1 반환
}
}
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {
private String uuid;
private String requestURL;
public void setRequestURL(String requestURL) {
this.requestURL = requestURL;
}
public void log(String message) {
System.out.println("[" + uuid + "]" + "[" + requestURL + "] " + message);
}
@PostConstruct
public void init() {
uuid = UUID.randomUUID().toString();
System.out.println("[" + uuid + "] request scope bean create");
}
}
자바는 객체지향, 플랫폼 독립적인 프로그래밍 언어
| 특징 | 설명 | 장점 |
|---|---|---|
| 플랫폼 독립성 | "Write Once, Run Anywhere" | 다양한 OS에서 실행 가능 |
| 객체지향 | 캡슐화, 상속, 다형성, 추상화 지원 | 코드 재사용성, 유지보수성 향상 |
| 자동 메모리 관리 | 가비지 컬렉션 제공 | 메모리 누수 방지 |
| 강한 타입 검사 | 컴파일 시 타입 오류 검출 | 런타임 오류 감소 |
| 멀티스레딩 | 동시성 프로그래밍 지원 | 성능 향상 |
| 보안성 | 포인터 없음, 바이트코드 검증 | 안전한 실행 환경 |
┌─────────────────────────────────────┐
│ Java 소스코드 │
│ (.java) │
└─────────────┬───────────────────────┘
│ javac 컴파일러
▼
┌─────────────────────────────────────┐
│ 바이트코드 │
│ (.class) │
└─────────────┬───────────────────────┘
│ JVM
▼
┌─────────────────────────────────────┐
│ 플랫폼별 기계어로 변환 │
│ (Windows, Linux, Mac 등) │
└─────────────────────────────────────┘
"자바의 가장 큰 특징은 플랫폼 독립성입니다. JVM 위에서 동작하여 '한 번 작성하면 어디서든 실행'이 가능합니다. 또한 객체지향 언어로서 캡슐화, 상속, 다형성을 지원하여 코드의 재사용성과 유지보수성이 뛰어납니다. 자동 메모리 관리를 통해 개발자가 메모리 관리에 신경 쓸 필요가 없고, 강한 타입 검사로 컴파일 시점에 오류를 잡아 안정성을 보장합니다."
OOP(Object-Oriented Programming) - 객체 지향 프로그래밍으로, 현실 세계의 사물을 객체로 모델링하여 프로그래밍하는 방법론
| 특징 | 정의 | 설명 |
|---|---|---|
| 캡슐화(Encapsulation) | 데이터와 메서드를 하나로 묶기 | 정보 은닉, 보안성 향상 |
| 상속(Inheritance) | 기존 클래스를 확장하여 새 클래스 생성 | 코드 재사용성 증가 |
| 다형성(Polymorphism) | 같은 인터페이스로 다른 동작 수행 | 유연성, 확장성 향상 |
| 추상화(Abstraction) | 복잡한 내부 구현을 숨기고 인터페이스만 제공 | 복잡성 감소 |
// 추상화
abstract class Animal {
public abstract void makeSound();
}
// 상속
class Dog extends Animal {
// 다형성 (메서드 오버라이딩)
@Override
public void makeSound() {
System.out.println("멍멍!");
}
}
// 캡슐화
class Car {
private String engine; // private 변수
public void startEngine() { // public 메서드
engine = "Running";
System.out.println("엔진 시동");
}
}
| 장점 | 설명 |
|---|---|
| 모듈화 | 객체 단위로 개발하여 독립적 개발 가능 |
| 재사용성 | 상속과 구성을 통한 코드 재사용 |
| 유지보수성 | 객체별 독립적 수정 가능 |
| 확장성 | 새로운 기능 추가가 용이 |
| 협업 | 역할 분담이 명확함 |
"OOP는 현실 세계의 사물을 객체로 모델링하여 프로그래밍하는 방법론입니다. 캡슐화, 상속, 다형성, 추상화라는 4가지 특징을 가지고 있습니다. 장점으로는 코드의 재사용성, 유지보수성, 모듈화, 확장성이 뛰어나며, 대규모 소프트웨어 개발에 적합합니다."
| 구분 | 정의 | 특징 |
|---|---|---|
| Overloading (오버로딩) | 같은 이름의 메서드를 매개변수만 다르게 여러 개 정의 | 컴파일 시점 결정 |
| Overriding (오버라이딩) | 부모 클래스의 메서드를 자식 클래스에서 재정의 | 런타임 시점 결정 |
| 구분 | Overloading | Overriding |
|---|---|---|
| 발생 위치 | 같은 클래스 내 | 상속 관계의 부모-자식 클래스 |
| 매개변수 | 반드시 달라야 함 | 반드시 같아야 함 |
| 리턴 타입 | 상관없음 | 같거나 하위 타입 |
| 접근 제어자 | 상관없음 | 같거나 더 넓은 범위 |
| 시점 | 컴파일 타임 | 런타임 |
| 다형성 | 정적 다형성 | 동적 다형성 |
public class Calculator {
// 매개변수 개수가 다름
public int add(int a, int b) {
return a + b;
}
public int add(int a, int b, int c) {
return a + b + c;
}
// 매개변수 타입이 다름
public double add(double a, double b) {
return a + b;
}
}
class Animal {
public void makeSound() {
System.out.println("동물이 소리를 냅니다");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("멍멍!"); // 부모 메서드 재정의
}
}
"오버로딩은 같은 클래스 내에서 같은 이름의 메서드를 매개변수만 다르게 여러 개 정의하는 것이고, 오버라이딩은 상속 관계에서 부모 클래스의 메서드를 자식 클래스에서 재정의하는 것입니다. 오버로딩은 컴파일 시점에, 오버라이딩은 런타임 시점에 어떤 메서드를 호출할지 결정됩니다."
객체(Object) - 클래스라는 설계도를 기반으로 실제 메모리에 생성된 인스턴스
| 구분 | 설명 | 예시 |
|---|---|---|
| 클래스 | 객체를 만들기 위한 설계도, 템플릿 | Car 클래스 |
| 객체 | 클래스를 기반으로 생성된 실체 | myCar, yourCar |
| 인스턴스 | 메모리에 할당된 객체 | new Car() |
public class Car {
// 속성 (필드, 멤버 변수)
private String color;
private int speed;
// 행위 (메서드)
public void start() {
System.out.println("시동을 걸다");
}
public void accelerate() {
speed += 10;
}
}
// 객체 생성과 사용
Car myCar = new Car(); // 객체 생성
myCar.start(); // 메서드 호출
| 특징 | 설명 |
|---|---|
| 상태(State) | 객체의 속성값으로 표현 |
| 행위(Behavior) | 객체가 수행할 수 있는 동작 |
| 식별성(Identity) | 다른 객체와 구별되는 고유한 특성 |
"객체는 클래스라는 설계도를 기반으로 메모리에 생성된 실체입니다. 객체는 상태를 나타내는 속성(필드)와 행위를 나타내는 메서드로 구성됩니다. 예를 들어, Car 클래스에서 myCar라는 객체를 생성하면, 이는 고유한 색상과 속도를 가지며 시동을 걸거나 가속할 수 있는 독립적인 실체가 됩니다."
| 용어 | 정의 | 관계 |
|---|---|---|
| Object | 클래스로부터 생성된 실체 | 추상적 개념 |
| Instance | 메모리에 할당된 객체 | 구체적 개념 |
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
// 객체 생성 과정
Person person1 = new Person("김철수", 25); // person1은 Person 객체의 인스턴스
Person person2 = new Person("이영희", 30); // person2는 또 다른 인스턴스
┌─────────────────────────┐
│ Heap 영역 │
│ ┌─────────────────────┐ │
│ │ Person 인스턴스 │ │ ← person1이 참조
│ │ name: "김철수" │ │
│ │ age: 25 │ │
│ └─────────────────────┘ │
│ ┌─────────────────────┐ │
│ │ Person 인스턴스 │ │
│ │ name: "이영희" │ │ ← person2가 참조
│ │ age: 30 │ │
│ └─────────────────────┘ │
└─────────────────────────┘
Person person = new Person("김철수", 25);
// instanceof로 인스턴스 타입 확인
if (person instanceof Person) {
System.out.println("person은 Person의 인스턴스입니다");
}
if (person instanceof Object) {
System.out.println("person은 Object의 인스턴스입니다"); // 모든 객체는 Object의 인스턴스
}
JDBC(Java Database Connectivity) - 자바 애플리케이션에서 데이터베이스에 연결하고 SQL을 실행하는 자바 API
| 구분 | 설명 |
|---|---|
| 목적 | 자바와 데이터베이스 간의 독립적인 연결 |
| 위치 | java.sql 패키지 |
| 특징 | 데이터베이스 독립적 |
┌─────────────────────────┐
│ Java Application │
│ │
└─────────┬───────────────┘
│ JDBC API 호출
▼
┌─────────────────────────┐
│ JDBC API │
│ (java.sql 패키지) │
└─────────┬───────────────┘
│
▼
┌─────────────────────────┐
│ JDBC Driver │
│ (MySQL, Oracle 등) │
└─────────┬───────────────┘
│
▼
┌─────────────────────────┐
│ Database │
│ (MySQL, Oracle 등) │
└─────────────────────────┘
| 구성 요소 | 역할 | 예시 |
|---|---|---|
| DriverManager | 드라이버 관리, 연결 설정 | 연결 URL로 DB 선택 |
| Connection | 데이터베이스 연결 객체 | SQL 실행을 위한 연결 |
| Statement | SQL 실행 객체 | SELECT, INSERT 등 |
| ResultSet | 쿼리 결과 저장 | SELECT 결과 처리 |
// 1. 드라이버 로드
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 연결 설정
String url = "jdbc:mysql://localhost:3306/mydb";
Connection conn = DriverManager.getConnection(url, "root", "password");
// 3. Statement 생성
Statement statement = conn.createStatement();
// 4. SQL 실행 및 ResultSet 처리
ResultSet resultSet = statement.executeQuery("SELECT * FROM users");
// 5. 결과 처리
while (resultSet.next()) {
System.out.println(resultSet.getString("name"));
}
// 6. 자원 해제
resultSet.close();
statement.close();
conn.close();
// MySQL 드라이버 로드
Class.forName("com.mysql.cj.jdbc.Driver");
// Oracle 드라이버 로드
Class.forName("oracle.jdbc.driver.OracleDriver");
// PostgreSQL 드라이버 로드
Class.forName("org.postgresql.Driver");
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC";
String username = "root";
String password = "password";
Connection connection = DriverManager.getConnection(url, username, password);
// 일반 Statement
Statement statement = connection.createStatement();
// PreparedStatement (권장)
String sql = "SELECT * FROM users WHERE age > ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 18);
// SELECT 쿼리 실행
ResultSet resultSet = preparedStatement.executeQuery();
// 결과 처리
while (resultSet.next()) {
System.out.println("Name: " + resultSet.getString("name"));
}
// INSERT, UPDATE, DELETE 실행
int affectedRows = preparedStatement.executeUpdate();
System.out.println("영향받은 행의 수: " + affectedRows);
// 순서 중요: ResultSet → Statement → Connection
if (resultSet != null) resultSet.close();
if (preparedStatement != null) preparedStatement.close();
if (connection != null) connection.close();
import java.sql.*;
public class JDBCExample {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 1단계: 드라이버 로드
Class.forName("com.mysql.cj.jdbc.Driver");
// 2단계: 연결 설정
String url = "jdbc:mysql://localhost:3306/testdb";
conn = DriverManager.getConnection(url, "root", "password");
// 3단계: PreparedStatement 생성
String sql = "SELECT * FROM users WHERE age > ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 20);
// 4단계: 쿼리 실행 및 결과 처리
rs = pstmt.executeQuery();
while (rs.next()) {
System.out.println("Name: " + rs.getString("name"));
}
} catch (SQLException e) {
System.out.println("데이터베이스 오류: " + e.getMessage());
} finally {
// 5단계: 자원 해제
try {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public class ModernJDBCExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/testdb";
String sql = "SELECT * FROM users WHERE age > ?";
try (Connection conn = DriverManager.getConnection(url, "root", "password");
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, 18);
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
System.out.println("Name: " + rs.getString("name"));
}
}
} catch (SQLException e) {
e.printStackTrace();
}
// 자동으로 자원 해제됨
}
}
| 기술 | 정의 | 특징 |
|---|---|---|
| Servlet | 자바를 사용하여 웹페이지를 동적으로 생성하는 서버측 프로그램 | Java 코드 안에 HTML |
| JSP | HTML 안에 자바 코드를 삽입하여 동적 웹페이지를 생성하는 기술 | HTML 코드 안에 Java |
| 구분 | Servlet | JSP |
|---|---|---|
| 코드 작성 | Java 코드 안에 HTML | HTML 안에 Java 코드 |
| 개발 속도 | 느림 | 빠름 |
| 유지보수 | 어려움 | 쉬움 |
| 컴파일 | 수동 컴파일 필요 | 자동 컴파일 |
| 성능 | 빠름 | 상대적으로 느림 |
| 디버깅 | 쉬움 | 어려움 |
| 역할 | 비즈니스 로직 처리 | 프레젠테이션 |
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
String name = request.getParameter("name");
// HTML을 Java 코드로 작성
out.println("<html>");
out.println("<head><title>Hello Servlet</title></head>");
out.println("<body>");
out.println("<h1>안녕하세요, " + name + "님!</h1>");
out.println("</body>");
out.println("</html>");
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>Hello JSP</title>
</head>
<body>
<%
// Java 코드를 HTML 안에 삽입
String name = request.getParameter("name");
if (name == null) {
name = "익명";
}
%>
<h1>안녕하세요, <%= name %>님!</h1>
<% if (name.equals("관리자")) { %>
<p>관리자 권한으로 로그인했습니다.</p>
<% } %>
</body>
</html>
클라이언트 요청 → 웹 컨테이너 → Servlet 클래스 로드
→ 인스턴스 생성 → service() 메서드 호출 → 응답 생성
클라이언트 요청 → 웹 컨테이너 → JSP를 Servlet으로 변환
→ 컴파일 → 실행 → 응답 생성
// JSP가 자동으로 생성하는 Servlet 코드 예시
public class hello_jsp extends HttpJspBase {
public void _jspService(HttpServletRequest request,
HttpServletResponse response) {
// JSP 내용이 여기에 변환됨
}
}
Controller (Servlet) ← → Model (JavaBean) ← → View (JSP)
↓ ↑
비즈니스 로직 처리 화면 표시
↓ ↑
데이터 처리 및 흐름 제어 사용자 인터페이스
// Get
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>Hello, World!</h1>");
out.println("</body></html>");
}
}
// Post
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>Hello, World!</h1>");
out.println("</body></html>");
}
}
클래스파일은 자바 소스코드를 컴파일한 후 생성되는 바이트코드 파일입니다. 클래스파일은 자바 가상 머신(JVM)에서 실행될 수 있는 형태로 변환됩니다.
public class Car {
private String model;
private int year;
public Car(String model, int year) {
this.model = model;
this.year = year;
}
public void drive() {
System.out.println("Driving a " + year + " " + model);
}
}
public class SportsCar extends Car {
public SportsCar(String model, int year) {
super(model, year);
}
@Override
public void drive() {
System.out.println("Driving a " + year + " " + model + " fast!");
}
}
public class FamilyCar extends Car {
public FamilyCar(String model, int year) {
super(model, year);
}
@Override
public void drive() {
System.out.println("Driving a " + year + " " + model + " safely!");
}
}
인터페이스는 클래스가 구현해야 하는 메서드를 정의하는 추상 타입입니다. 인터페이스는 클래스가 구현해야 하는 메서드를 정의하며, 클래스는 인터페이스를 구현해야 합니다.
public interface Flyable {
void fly();
}
public class Bird implements Flyable {
@Override
public void fly() {
System.out.println("Bird is flying");
}
}
public class Plane implements Flyable {
@Override
public void fly() {
System.out.println("Plane is flying");
}
}
JUnit은 자바 프로그래밍을 위한 테스트 프레임워크입니다. Mock 이용시 사용하는 프레임워크는 Mockito입니다.
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class UserServiceTest {
@Test
public void testUserService() {
UserRepository userRepository = mock(UserRepository.class);
when(userRepository.findById(1L)).thenReturn(Optional.of(new User(1L, "John Doe")));
UserService userService = new UserService(userRepository);
User user = userService.getUserById(1L);
assertEquals("John Doe", user.getName());
}
}
JUnit 테스트 데이터를 일일히 만들어 줄 수 없을 때 이용할 수 있는 방법은 데이터 빌더를 사용하는 것입니다.
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class UserServiceTest {
@Test
public void testUserService() {
UserRepository userRepository = mock(UserRepository.class);
when(userRepository.findById(1L)).thenReturn(Optional.of(new User(1L, "John Doe")));
UserService userService = new UserService(userRepository);
User user = userService.getUserById(1L);
assertEquals("John Doe", user.getName());
}
}
JPA(Java Persistence API)는 자바 애플리케이션을 위한 퍼시스턴스 API입니다. Persistence Context는 JPA를 사용하여 데이터베이스와 통신할 때 사용되는 객체의 컬렉션입니다.
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
public class UserService {
private final EntityManager entityManager;
public UserService(EntityManager entityManager) {
this.entityManager = entityManager;
}
public void saveUser(User user) {
EntityTransaction transaction = entityManager.getTransaction();
try {
transaction.begin();
entityManager.persist(user);
transaction.commit();
} catch (Exception e) {
transaction.rollback();
throw e;
}
}
}