위키백과에서는 이렇게 서술되어있다. 프레임워크(Framework)는 생소하다 뭘까?
일반적으로 프레임워크는 '뼈대'를 의미하며 어떤 목적을 가지고 라이브러리들이 합쳐진 상위의 그룹이라고 한다.
작년에 내가 스프링을 배우려고 했던 계기를 떠올려보면 그렇다. 구직하기 용이하다고 판단했으니까 배우려고했다.
나는 스프링외의 다른 프레임워크를 접해본적이 없다. 이 다음부터는 합리적인 추측일 뿐이다.
내가 자바를 알기 전부터 자바 사용률은 항상 1위었다. 높은 사용률의 가장 큰 이유는 아무래도 플랫폼에 종속되지 않는 자바의 특징 때문이리라. 현재는 좀 주춤한듯 하지만 여전히 많이 쓰이는 언어다. 그렇다면 기업에서도 자바를 사용하는 사람이 가장 구인하기 쉬웠을 것이고 자연스럽게 자바 기반 프레임워크인 스프링을 채택했을 것이라고 생각한다.
스프링은 코드 한 줄이면 쉽게 원하는 라이브러리를 쓸 수 있다.
세팅하는 시간을 엄청나게 줄이는 이점이고 사용률에 한몫 하지 않았을까
🤔 POJO 들어보긴 했는데 이게 뭘까?
나는 다음 두 가지로 POJO의 의미를 객체지향적이다라고 받아들였다.
객체를 직접 관리하므로 객체지향적 설계(이하 SOLID)를 지킬 수 있다.
단일 책임 원칙(SRP) : 모든 클래스는 하나의 일만 하자는 것이다.
이는 스프링이 객체를 생성하는 책임을 지게됨으로써 성립한다.
개방 폐쇄 원칙(OCP) : 확장은 열려있고 변경은 닫혀있다.
하나의 소프트웨어 구성요소가 변경된다고 해서 다른 구성요소에 영향을 주면 안 된다는 의미다.
인터페이스의 구현체를 변경하려면 생성하는 모든 클래스를 변경해야하는 것을
스프링이 객체를 생성 및 주입함으로써 성립한다.
의존관계 역전 원칙(DIP) : 구현 클래스가 아닌 인터페이스에 의존하라는 것이다.
인터페이스가 구현체를 알 필요는 없다는 것이며 OCP를 위반하면 DIP또한 위반된다.
마찬가지로 스프링이 객체를 생성 및 주입함으로써 성립한다.
관점 지향 프로그래밍(AOP : Aspect-Oriented Programming)을 지원한다.
SOLID 원칙을 따르면서 기능별로 클래스를 분리하게되어
로깅, 보안, 트랜잭션등 공통된 부분(Aspect)이 흩어져 존재하는데
이러한 부분과 비지니스 로직을 프록시 방식으로 분리시켜 객체 지향 프로그래밍(OOP)을 보완한다.
🤔스프링이 객체를 관리해주면서 얻는 이점이 뭘까?
결합도가 내려간다.
인터페이스의 구현체를 변경하면 그 인스턴스와 관련된 모든 클래스를 수정해야하는데
스프링이 관리해주게 되면 스프링에서 일괄적으로 변경된 인스턴스로 주입해주면서
해당 클래스가 다른 인스턴스를 생성해야하는 책임을 컨테이너에 넘기고(IoC, SRP)
다른 클래스를 수정할 필요가 없어진다(OCP, DIP).
싱글톤 패턴의 문제점을 해결할 수 있다.
Enum을 이용하면 동시성 문제, Reflection 문제를 해결할 수 있지만
테스트 환경에서 mock으로 대체하기가 어렵다는 문제점이 남는다.
하지만 스프링의 IoC(Inversion of Control) 컨테이너가 클래스의 제어권을 가지고있기 때문에
Singletone registry를 이용해 기본적으로 평범한 생성자를 가진 객체를 싱글톤으로 존재하게 한다.
스프링을 배우려는 사람은 백엔드 개발자고 백엔드 개발자가 하는 역할은 DB를 이용해서 데이터를 가공하고 통신하는 것이라 생각한다.
스프링이 가장 편하게 해주는 건 DB 설계와 사용이 아닐까?
객체와 RDBMS를 매핑시켜주는 기술이다. 자바 진영에서는 ORM의 인터페이스를 모아 JPA를 만들었고 스프링에서는 JPA를 쓰기 쉽도록 Spring data로 매핑을 추상화해서 사용하고있다. 이게 어떤 부분에서 편하게 만들어줬는가?
JPA를 사용하기 전이었던 나는 기존에 mybatis mapper를 이용했었다.
//repository
@Mapper
public interface UserMapper {
void join(JoinRequestDto requestDto) throws Exception;
}
매핑할 DAO를 만들고
//mapper-xml
<mapper namespace="com.ssafy.happyHouse.repository.UserMapper">
<insert id="join" parameterType="JoinRequestDto">
insert into happyHouse_member (userId, userPw, userName, email)
values (#{userId}, #{userPw}, #{userName}, #{email})
</insert>
</mapper>
xml에 sql 쿼리문을 작성했다.
//Mybatis test
@Transactional
@SpringBootTest
@Commit
public class MybatisUserTest {
@Autowired
UserService userService;
@Test
@DisplayName("마이바티스 회원 가입 테스트")
public void mybatisJoin() throws Exception {
JoinRequestDto requestDto = new JoinRequestDto("KimDongKyu", "AnJiYEAH", "ParkSeongHo", "NO");
userService.join(requestDto);
}
}
이러한 방식은 내가 DAO를 수정하면 sql 쿼리문도 수정해야하는 의존성을 낳았고 이는 객체지향적이지 못하다고 생각한다.
JPA를 이용하면 Repository의 메소드명으로 매핑이되고 내가 굳이 xml에 sql 쿼리문을 작성하지 않아도 된다.
다음은 Repository 인터페이스와 JPA 구현체다.
public interface UserRepository {
public User save(User user);
}
@RequiredArgsConstructor
public class JpaUserRepository implements UserRepository{
private final EntityManager em;
@Override
public User save(User user) {
em.persist(user);
return user;
}
}
간단한 CRUD는 더이상 SQL 쿼리문을 작성할 필요도 없어지고 작성하기가 편해졌다.
더 나아가서 Spring data jpa를 이용하면 다음과 같다
public interface SpringDataJpaUserRepository extends JpaRepository<User, Long>, UserRepository{
}
공통적으로 생각할 법한 CRUD는 Spring data jpa에 들어있고 이 인터페이스를 사용할 때
스프링이 알아서 구현체를 만들어서 넣어준다. 중복되는 코드가 줄어들 것이고 생산성이 높아질 수 밖에 없을 것 같다.