extends는 클래스나 인터페이스를 상속할 때 사용된다. 클래스를 상속하면 부모 클래스의 필드와 메서드를 자식 클래스에서 물려받을 수 있다. 인터페이스를 상속할 때도 같은 방식으로 부모 인터페이스의 메서드 선언을 물려받을 수 있다.
클래스 상속 : class Child extends Parent
인터페이스 상속 : interface MyRepository extends JpaRepository
// 부모 클래스 Animal
class Animal {
public void sound() {
System.out.println("Animal makes a sound");
}
}
// 자식 클래스 Dog가 Animal을 상속받음
class Dog extends Animal {
// sound 메서드를 재정의 (Override)
@Override
public void sound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.sound(); // "Dog barks" 출력
}
}
여기서 Dog클래스는 Animal클래스를 상속(extends)하여 부모 클래스인 Animal의 메서드를 물려받고, 필요에 따라 재정의(override)하여 새로운 동작을 정의한다.
// 부모 인터페이스 AnimalActions
interface AnimalActions {
void eat();
}
// 자식 인터페이스 PetActions가 AnimalActions를 상속받음
interface PetActions extends AnimalActions {
void play();
}
// Dog 클래스가 PetActions 인터페이스를 구현
class Dog implements PetActions {
public void eat() {
System.out.println("Dog is eating");
}
public void play() {
System.out.println("Dog is playing");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // "Dog is eating" 출력
dog.play(); // "Dog is playing" 출력
}
}
PetActions 인터페이스는 AnimalActions인터페이스를 상속(extends)한다. 그 결과, PetActions를 구현하는 클래스는 AnimalActions와 PetActions에 정의된 모든 메서드를 구현해야 한다.
implements는 인터페이스를 구현할 때 사용되며, 인터페이스에 선언된 메서드를 반드시 구현해야한다.
인터페이스 구현 : class MyService implements SomeInterface
// 인터페이스 AnimalActions
interface AnimalActions {
void eat();
void sleep();
}
// Dog 클래스가 AnimalActions 인터페이스를 구현
class Dog implements AnimalActions {
// 인터페이스에 선언된 메서드들을 구현
public void eat() {
System.out.println("Dog is eating");
}
public void sleep() {
System.out.println("Dog is sleeping");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // "Dog is eating" 출력
dog.sleep(); // "Dog is sleeping" 출력
}
}
Dog클래스는 AnimalActions 인터페이스를 구현(implements)합니다. 인터페이스에서 선언된 eat과 sleep 메서드를 반드시 구현해야한다.
// 첫 번째 인터페이스 Flyable
interface Flyable {
void fly();
}
// 두 번째 인터페이스 Swimmable
interface Swimmable {
void swim();
}
// Duck 클래스가 Flyable, Swimmable 인터페이스를 구현
class Duck implements Flyable, Swimmable {
public void fly() {
System.out.println("Duck is flying");
}
public void swim() {
System.out.println("Duck is swimming");
}
}
public class Main {
public static void main(String[] args) {
Duck duck = new Duck();
duck.fly(); // "Duck is flying" 출력
duck.swim(); // "Duck is swimming" 출력
}
}
여기서는 Duck클래스가 두 개의 인터페이스 Flyable과 Swimmable을 구현(implements)하고, 각 인터페이스에 선언된 메서드를 모두 구현하고 있다.
extends는 클래스 상속이나 인터페이스 상속에 사용되며, 기존의 기능을 물려받아 사용할 수 있습니다.
implements는 클래스가 인터페이스를 구현할 때 사용되며, 인터페이스에 정의된 메서드를 반드시 구현해야 합니다.
스프링 데이터 JPA에서는 엔티티에 대한 기본적인 데이터 처리를 위해 JpaRepository와 같은 미리 정의된 리포지토리를 확장합니다. 이렇게 확장된 리포지토리는 스프링이 자동으로 구현체를 생성해주므로, 직접 CRUD 메서드를 구현할 필요 없이 필요한 데이터 처리 기능을 사용할 수 있다.
import org.springframework.data.jpa.repository.JpaRepository;
// 엔티티 클래스 (예시: Board)
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
// Getter, Setter, Constructors (생략)
}
// JpaRepository를 확장하는 리포지토리 인터페이스
public interface BoardRepository extends JpaRepository<Board, Long> {
// 기본적으로 제공되는 CRUD 메서드를 바로 사용할 수 있음
}
JpaRepository<Board, Long>는 두 가지 매개변수를 받는다.
1. Board : 데이터베이스 테이블과 매핑된 엔티티 클래스
2. Long : 엔티티의 ID타입을 지정한다. Board 엔티티에서 id필드는 Long타입이므로 이를 지정한다.
이렇게 JpaRepository를 확장함으로써 BoardRepository는 스프링이 자동으로 구현해주는 아래의 메서드들을 사용할 수 있게된다.
이렇게 리포지토리 인터페이스를 확장한 후, 이를 서비스 또는 컨트롤러에서 활용하여 간편하게 데이터베이스 작업을 처리할 수 있다.
서비스에서 메소드들 사용하는 예시
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BoardService {
// BoardRepository를 의존성 주입
@Autowired
private BoardRepository boardRepository;
// 새로운 게시글 저장
public Board saveBoard(Board board) {
return boardRepository.save(board);
}
// 모든 게시글 조회
public List<Board> getAllBoards() {
return boardRepository.findAll();
}
// 특정 게시글 조회
public Board getBoardById(Long id) {
return boardRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Invalid board ID: " + id));
}
// 게시글 삭제
public void deleteBoard(Long id) {
boardRepository.deleteById(id);
}
}
JpaRepository에서 제공하는 기본 메서드 외에도, 인터페이스에서 추가적으로 사용자 정의 메서드를 선언할 수 있다. Spring Data JPA는 메서드 이름을 기반으로 자동으로 SQL 쿼리를 생성하는 기능을 지원한다.
예를 들어, 게시글 제목으로 검색하는 메서드를 정의
public interface BoardRepository extends JpaRepository<Board, Long> {
// 제목으로 게시글을 검색하는 메서드 정의
List<Board> findByTitle(String title);
}
이렇게 메서드 이름을 기반으로 정의하면, 스프링이 자동으로 해당 쿼리를 생성하고 실행해 준다.
Spring Data JPA는 메서드 이름 기반 쿼리 외에도 JPQL(Java Persistence Query Language)를 사용하여 사용자 정의 쿼리를 작성할 수 있다.
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface BoardRepository extends JpaRepository<Board, Long> {
// JPQL을 사용한 사용자 정의 쿼리
@Query("SELECT b FROM Board b WHERE b.title = :title")
List<Board> findByTitleCustom(@Param("title") String title);
}
요약해보면,
1. JpaRepository를 extends하여 기본 CRUD 메서드를 사용할 수 있다.
2. 추가적인 메서드를 선언하여 데이터 조회 조건을 정의할 수 있다.
3. JPQL을 사용해 복잡한 쿼리도 작성할 수 있다.
스프링 데이터 JPA의 확장 기능을 활용하면 데이터베이스와의 상호작용을 매우 간단히 처리할 수 있고, extends는 그 핵심 역할을 수행하는 문법이다.