Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

전공공부

[Spring] PSA(Portable Service Abstraction) + DI(Dependency Injection) 본문

Study/Spring Boot

[Spring] PSA(Portable Service Abstraction) + DI(Dependency Injection)

monitor 2024. 1. 19. 17:09
PSA (Portable Service Abstraction)

 

PSA (Portable Service Abstraction)는 스프링에서 서비스 추상화를 구현하는 개념입니다. 이는 특정 기술에 종속되지 않고, 일관된 인터페이스를 제공하여 서비스들을 편리하게 교체하고 테스트하기 위한 목적으로 도입되었습니다.

 

 

그렇다면 이 예제가 무엇일까요?

 

import com.test.tester.portableServiceAdapter.dao.UserDao;
import com.test.tester.portableServiceAdapter.dto.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImpl implements UserDao {

    private final JdbcTemplate jdbcTemplate;

    public UserDaoImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public void createUserTable() {
        jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS users (id SERIAL, name VARCHAR(255))");
    }

    @Override
    public void insertUser(User user) {
        jdbcTemplate.update("INSERT INTO users (name) VALUES (?)", user.getName());
    }

    @Override
    public User getUserById(Long id) {
        return jdbcTemplate.queryForObject("SELECT * FROM users WHERE id = ?", new Object[]{id}, (rs, rowNum) ->
                new User(rs.getLong("id"), rs.getString("name")));
    }
}

 

public interface UserDao {
    void createUserTable();
    void insertUser(User user);
    User getUserById(Long id);
}

 

위 처럼 Dao 클래스 구현체를 직접 구현해서 쓰는 것 보다 인터페이스 하나 잘 만들어서 가져와서 쓰고 의존성 주입을 해준다면 편리하게 우리는 쓸 수 있습니다. 그리고, 이미 JPA와 같이 추상화 된 것들에 의해서 이미 코드들이 잘 작동하고 있습니다. 예를 들면 우리는 직접 @Transactional을 구현 할 필요 없이 low level에서 잘 구현 된 것을 우리는 어노테이션을 불러서 사용하고 있죠.

 

(사실 위 코드도 완전히 추상화 된 것을 쓰려면 JPA로 구현 된 것을 그대로 쓰는 것이 좋겠지만 위와 같이 사용하겠습니다.)

 

또한, PSA로 잘 인터페이스를 둔 것 단위테스트가 쉽다는 장점이 있는데요.

 

아래 예시를 보시죠

 

@SpringBootTest
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @MockBean
    private UserDao userDao;

    @Test
    public void testUserService() {
        // Mock 객체를 이용하여 userDao의 행동 정의
        when(userDao.getUserById(1L)).thenReturn(new User(1L, "Mock User"));

        // UserService의 특정 메서드 호출
        User retrievedUser = userService.getUserById(1L);

        // 결과 검증
        assertThat(retrievedUser).isNotNull();
        assertThat(retrievedUser.getName()).isEqualTo("Mock User");
    }
}

 

만일, 서비스 단에서 복잡한 DB 접근 구조를 모두 구성하고 만들어야 한다면 우리는 DB 단의 구성까지도 모두 알아서 구현하고 호출하는 코드를 짜야겠지만 위와 같이 간단하게 단위테스트를 실행 할 수 있습니다.

 

위 코드는 제어권이 상위 템플릿으로 역전이 적절히 잘 되었고 (IoC) 의존성 주입을 interface로 실행하여 만일 다른 객체를 의존하는 코드를 만들었을때 그 객체가 바뀜으로 인해서 일어나는 일의 변화에 대해서 적절한 대응이 가능한 코드가 되었습니다.

 

 

 

 

추가로 만일 DI를 제대로 하지 않으면 아래 처럼 나오게 됩니다.

 

DI (Dependency Injection)
public class UserService {

    private UserRepository userRepository;

    public UserService() {
        // 이렇게 직접 인스턴스를 생성하는 것은 의존성 주입을 지키지 않는 것입니다.
        this.userRepository = new UserRepository();
    }

    // UserService의 나머지 로직...
}

public class UserRepository {

    // UserRepository의 로직...
}

 

이따위의 코드가 나오게되고 실제로 이는 UserRepository의 생성자에서 생성하는 것이 바뀔때 마다 의존적입니다.

 

public class UserService {

    private UserRepository userRepository;

    // 생성자를 통해 UserRepository를 주입받도록 변경
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // UserService의 나머지 로직...
}

public class UserRepository {

    // UserRepository의 로직...
}

 

이를 내 객체에서 의존성을 주입을하게 되면 의존성을 낮출 수 있습니다.

 

참고로, 스프링에서 추천하는 방식은 인터페이스 implementaion 보다는 생성자를 통해서 생성하는 방식을 권하고 있습니다.

 

+) 실제로 이 포스팅을 보시는 여러분들이 SI 기업에 취직하게 된다면 간간히 이런 기본적인 룰을 지키지 않고 실행하는 경우가 종종 있습니다. 그럴때마다 왜 그것을 써야 하는지 정확하게 알고 이걸 쓸 때의 장점을 잘 설명을 할 수 있어야 좀 더 세련된 코드를 구현 할 수 있습니다. 하지만, 기업 특성상 업무 룰이 먼저가 되기 때문에 따로 개인 레포를 만들거나 프로젝트를 진행하면서 세련된 코드를 짜는 연습을 해야 할 것 입니다.

 

위 코드를 참조하려면 아래 링크를 참고하면 됩니다.

 

https://github.com/1ComputerMaster/1ComputerMaster/tree/main/Study/tester