개요
벌써 취업해서 일한지 4주차이다. 나는 지금 하루가 다르게 성장하며 배우고 즐겁게 일하고 있다. 다만 시간이 부족할 뿐...! 입사하자 마자 4주차까지 SI로 일했고, 이제 드디어 자사 서비스 개발에 들어간다. 그래서 4주간 일했던 경험을 피드백으로 작성해볼 것이다.
평소 나는 ORM을 주로 사용했다. Spring에 DataJPA 또는 NestJS에 TypeORM으로 코드로 추상화 된 DB 작업만 했는데 여기에선 Spring에 xml 파일 기반의 MyBatis라는 DB Mapper를 사용했다. 4주간 이 덕분에 기술 부채로 남겨뒀던 SELECT 문을 맘껏 써봤다. 지금은 SELECT문 작성과 두 개 이상의 서브 쿼리, CTE(Common Table Expression)을 사용한 복잡한 조회 문을 손쉽게 구사할 수 있게 되었다. 회사 사람들 전부 좋은 사람들이고 잘 챙겨주셔서 사실 3주차에 이미 전부 적응해버렸고, 4주차에는 API를 무리 없이 개발 가능한 상황이 되었다. 본문에서는 주요 키워드를 미리 던지는 방식으로 이어나가겠다.
본문
ORM과 DB Mapper
ORM은 객체로써 Collection 등으로 코드 레벨에서 쉽게 관리가 가능한데, DB Mapper 중 하나인 MyBatis는 xml 파일에서 날 것의 SQL문과 동적으로 쿼리를 생성해주는 MyBatis 문법의 조합으로 사용한다. 물론 MyBatis도 Collection 형태로 반환하여 강력하게 사용할 수 있어서 자유도가 훨씬 높다. 그러나 유지 및 보수의 효율이 극히 떨어진다고 느끼는데, 이유는 MyBatis는 조회하는 칼럼이 변경이라도 하면 SQL을 직접 고쳐야 하는데 ORM은 객체의 필드(프로퍼티)만 변경해도 쉽게 고칠 수 있다. ORM은 보통 Repository라는 계층에서 관리하는데 반해 DB Mapper는 DAO(Data Access Object)라는 Mapper 객체에서 관리한다. 그리고 가장 아쉬운 점은 MyBatis는 100% 런타임에 쿼리를 실행해야 오류를 잡을 수 있다는 점이다. DataJPA나 QueryDSL 같은 기술은 컴파일 타임에도 문법 오류를 잡아준다. 이 장점이 개발자로 하여금 많은 개발 효율을 높여준다 생각한다.
기획서는 ERD와 교차 확인이 필요하므로 개발자가 무조건 검수해야 한다.
이번에 주로 구현하게 된 API는 목록 조회이다. 나에게 주어진 것은 ERD, 수많은 페이지가 그려진 기획서, 그리고 팀장님의 도메인 설명이다. 나는 이번에 무엇인가 개발자가 제대로 검수하지 않은 듯한 기획서를 받았다. 분명 규모가 있고, 절차를 갖고 있는 회사이나 개발단에서 많은 검수를 거치지 않은 듯한 느낌을 많이 받았다. 이유는 다음과 같다.
- API에서 원하는 데이터는 월별 데이터인데 일별 데이터만 저장한다. 해당 API를 조회할 때마다 월별 데이터를 가공해야 하기 때문에 성능이 좋지 않을 것이다.
- 거의 모든 API가 적어도 4번의 JOIN이 필요함. 많으면 10번의 JOIN까지 경험해봤다. 그렇다고 정규화가 잘 되어 있지는 않다. 중복 데이터를 저장하는 테이블도 상당히 많다.
- API랑 상관 없이 데이터가 파편화 되어있다. 때문에 서브 쿼리를 많이 사용할 수 밖에 없다. 그래도 여기까진 괜찮은데 서브쿼리를 아무리 사용해도 기획서에서 주어진 데이터로는 불합리한 코드를 짤 수 밖에 없는 것도 존재한다.
- ERD에는 있는데 개발 서버 DB에 테이블이 존재하지 않는다. 때문에 제대로 된 테스트가 쉽지 않다.
코드 상 문제 및 개선 방향
- 모든 구조가 레이어드 구조(Controller - Service - DAO)인데 싱크홀 안티 패턴이 무조건 나타난다. Controller에선 세션이나 요청에 대한 처리를 해주므로 괜찮은데, 모든 비즈니스 로직을 DAO에 매핑 되어 있는 xml 파일에서 진행하기 때문에 Service와 DAO는 이 메서드를 호출하기만 한다.
@Service
class ServiceImple{
// DAO 선언 등
@Override
public List<Map<String, Object>> selectList(VO vo) throws Exception {
return DAO.selectList(vo);
}
}
- VO(Value Object)를 DTO(Data Transfer Object)처럼 사용한다. 객체지향프로그래밍에서 가장 중요한 것은 작명이라고 생각한다. 그래야 이름에 맞는 역할과 책임을 부여할 수 있기 때문이다. VO는 동일한 속성을 사용하는 객체가 여럿 있을 때 유효성 검사를 각 객체에서 진행하지 않고, VO에서만 진행함으로써 중복 코드를 줄일 수 있고, Setter가 없기 때문에 해당 값을 믿고 사용할 수 있어야 한다. 그러나 여기에선 VO는 Setter도 존재하고, 단순히 계층 간의 데이터 이동으로써 사용하므로 DTO에 가깝다 생각했다.
public class VO {
// fields
// Getter, Setter
}
- 컨트롤러에서 HttpSurvletRequest 객체를 직접 받아 request.getParameter('key') 형태로 HTTP body에서 각 파라미터의 값을 추출한다. 때문에 한 줄, 한 줄 적어줘야 한다. 하지만 @RequestBody 같은 어노테이션을 사용하면 DTO를 만들어 객체로 쉽게 관리가 가능하기 때문에 코드의 양이 줄어들고, API 명세 변경 등으로 HTTP request Body가 변경 되었을 때 DTO만 변경하면 되므로 유지보수가 용이하다.
// HttpRequestSurvlet에서 직접 추출
@GetMapping
public void func1(HttpServletRequest request, ~) {
VO.setName(request.getParameter("name"));
// ..
// ..
// ..
// 파라미터가 늘어날 수록 코드가 길어짐
}
// 어노테이션 기반의 구현
@GetMapping
public void func2(@RequestBody DTO dto) {
// Request body의 내용에 상관 없이 따로 작성하지 않아도 됨
}
결론
난 처음 오자마자 SI로 일을 시작해서 많이 걱정했다. 자사 서비스를 개발하며 유지 보수 및 개선을 하는 경험을 더 하고 싶었던 과거의 생각 때문이다. 그러나 4주간 열심히 달리며 생각이 달라졌다. 신입 때는 일단 일을 진행하는 것 자체가 성장의 연속이다. 지금은 엄청나게 성장한 나를 보며 재미있고, 놀랍다. 주위 사람들이 현업에서 배우라고 하셨던 말씀이 이렇게 피부로 와닿아서 자극적일 만큼의 파급력을 가져올지는 몰랐는데 하루 하루 즐겁게 일하고 있다. 오늘부터는 자사 서비스 개발을 진행했다. 앞으로도 기술이라거나 도메인 정보 등을 정리해서 블로그에 포스팅 해볼 예정이다.
'개발 일지' 카테고리의 다른 글
socket.io 서버 소켓에서 연결된 클라이언트 소켓 ID와 입장한 room을 찾는 방법 (0) | 2024.08.02 |
---|---|
Socket.io이란? 웹 소켓과의 차이점은? (0) | 2024.07.19 |
API 명세서 작성 규칙(convention) (2) | 2024.02.12 |
[PA] User 관련 ERD 설계 (0) | 2024.02.01 |
Discord 웹후크를 통해 Github에서 일어나는 이벤트 감지하기 (0) | 2024.01.29 |