728x90

[오프라인 수료식 후기]

3주간의 시간이 지나고, 워밍업 클럽 스터디가 끝나면서 오프라인 수료식을 다녀왔다.

 

[첫번째 순서]

낯을 조금 가리는 탓에 처음 도착해서는 앉아서 멀뚱멀뚱 사무실만 구경하고 있었다.

감사하게도 같은 테이블에 계신 러너분께서 먼저 대화를 걸어주셔서 수료식 시작 전까지 신나게 대화하였고,

다른 러너 두분께서 추가로 오셔서 첫번째 순서인 저녁 식사를 하며 또 다시 수다 타임을 가졌다.

 

[두번째 순서]

식사를 마치고 두번째 순서는 지식 공유자분들의 Q&A 타임이었다.

질문은 사전에 러너분들께서 작성해주신 질문에서 몇개 골라 선정된 질문들이었다.

취업에 관한 질문, 개발자에 대한 질문 등등 여러가지 질문들이 있었는데, 그동안 내가 많이 고민했던 질문들이 많았고

내가 고민했던 것과 지식공유자분들이 답변해주신 것과 일치해서 조금은 안도(?) 하였다.🤣

 

[세번째 순서]

이후 세번째 순서는 우수러너 시상식이었다. 나는 처음부터 우수러너에 대한 기대는 크게 없었다.

블로그도 누군가에게 보여주기 보다는 내가 공부하고 기록하는 곳으로 사용했고 깔끔하게 작성하는 편도 아니다.

열심히 해보겠다는 의지는 강했지만 실력자분들이 많아 이런 쉬운 질문을 해도 될까? 라는 생각에 질문을 망설였다.

(조금 더 찾아보니 혼자서도 충분히 해결 가능했던 질문들이 많았다ㅎㅎ)

위와 같은 이유로 우수러너는 포기하였고, 완주와 내 실력 향상에 초점을 맞추어 묵묵히 열심히 하였다.

 

그런데!!

우수러너 명단에 내 이름이 있던 것이었다!!

감사하게도 우수러너로 선정되어 인프런의 귀여운 굿즈를 받게 되었다!

다시 한번 감사드립니다.

 

[마지막 순서]

그렇게 기쁜마음으로 시상식을 마치고 마지막 순서인 네트워킹을 가졌다.

조별로 나누어 앉았고 각 조별로 코치님과 인프런 개발자 한분씩 들어가 네트워킹을 가졌다.

내가 있던 테이블에는 인프런에서 5년째 일하고 계신 프론트엔드 개발자분이셨고,

현재 취업 시장이 어떤지, 현업에서는 어떻게 일하는지 등등 유익한 이야기를 나누며 수료식은 끝이 났다.

 

[스터디를 하면서 느낀점]

그동안 공부하면서 이해되지 않았던 부분이 이번 강의를 통해 이해가 되어 고구마 100개 먹은 가슴이 뻥 뚫렸고,

궁금한 부분에 대해 해결하고 고민하는 방법을 얻을 수 있어서 좋았다.

 

이전엔 오류가 나고 해당 매서드에 대해 궁금하면 무작정 인터넷을 먼저 찾았지만

이제는 해당 매서드에 들어가서 어떤 것들로 구성되어 있고, 해당 오류는 어떤 오류인지 공식문서나 해당 코드를 통해

먼저 생각하고 고민해보는 습관이 형성되어 좋았다.

 

짧다면 짧고 길다면 긴 3주동안 재미있게 공부하며 즐겼던 스터디였다.

 

스터디를 진행해주시고 늦은 시간까지 수료식을 위해 자리를 만들어주신 인프랩 임직원분들,

바쁘실텐데 중간 중간 깜짝 특강을 진행해주시고 예정된 시간이 지나도 목소리가 쉬어가며 질문과 정보 전달을 위해

열정을 보여주신 최태현 코치님께 감사인사 드립니다.

그리고 3주간 함께 달리신 러너분들 고생 많으셨습니다!😁

728x90
728x90

[3주차 학습내용]

이번 3주차에서는 JPA 연관관계 매핑하기 강의에서 만들었던 도서관리 프로그램 배포하기를 하였다.

JPA를 조금 더 객체지향적으로 코드를 고쳐볼 수 있었다.

 

[3주차 과제]

3주차에서 진행한 미니프로젝트는 출퇴근 사내 관리프로그램이었다.

JPA에 대한 개념이 많이 부족해 생각한 대로 프로그램은 돌아가는데 제대로 코드를 작성한게 맞는지, 코드는 깔끔하게 짰는지 등등 많은 생각이 들었다. 그래도 이번 강의를 통해서 JPA가 무엇인지 어떻게 사용하는지 그리고 스프링에 대한 개념에 대해 조금 더 와닿게 되었다.

미니프로젝트 👉 https://ddonydev.tistory.com/83

 

[마무리 및 느낀점]

오래된 취준 기간으로 약간의 강제성이 필요할 때 우연히 인프런 워밍업 클럽 스터디를 접하게 되었고, 현재 나에게 제일 필요했던 공부인 JPA를 공부할 수 있어 정말 뜻 깊은 스터디였다. 단순 강의만 듣는 것이 아닌 미션과 미니프로젝트를 병행하여서 강의 내용을 보다 더 쉽게 이해할 수 있었다.

이번 스터디로 습득한 지식을 바탕으로 조금 더 공부하여 나머지 2,3,4단계까지 마무리 해보고 개인 서버에 배포까지 해볼 예정이다.

 

강의 링크 👉 자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]

728x90
728x90

 

1. 팀

Table

 

Entity

@Entity
@Getter
public class Team {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    @Column(nullable = false, length = 20)
    private String name;
    @Column(nullable = false, length = 20)
    private String manager;
    @Column(nullable = false)
    private long employeeCnt;

    protected Team(){}

    public Team(String name, String manager, long employeeCnt) {
        this.name = name;
        this.manager = manager;
        this.employeeCnt = employeeCnt;
    }

    public void updateEmployeeCnt() {
        this.employeeCnt++;
    }

    public void updateManager(String name) {
        this.manager = name;
    }
}

 

1

Controller

@RestController
@RequestMapping("/attendance/team")
public class TeamController {
    private final TeamService service;

    public TeamController(TeamService service) {
        this.service = service;
    }

    @PostMapping("/resister_team")
    public void resisterTeam(@RequestBody RegisterTeamRequest request) {
        service.insertTeam(request);
    }
}

 

Service

@Service
public class TeamService {

    private final TeamRepository repository;

    public TeamService(TeamRepository repository) {
        this.repository = repository;
    }

    public void insertTeam(RegisterTeamRequest request) {
        repository.findByName(request.getName()).ifPresent(team -> {
            throw new IllegalArgumentException("이미 존재하는 팀 이름입니다.");
        });

        repository.save(new Team(request.getName(), 
        request.getManager(), request.getMemberCnt()));
    }
}

 

  • RegisterTeamRequest 객체에서 팀 이름을 꺼내와 등록된 팀이 있는지 확인해준다.
  • 팀이 등록 되어 있다면 Exception을 발생시키고 없다면 새로운 팀을 만들어준다.
  • 팀 등록은 이름, 매니저, 그리고 팀 인원수를 가진 Team 객체를 생성하여 저장된다.

 

Repository

public interface TeamRepository extends JpaRepository<Team, Long> {
     Optional<Team> findByName(String name);
}

 

 

DTO

@Getter
public class RegisterTeamRequest {
    private long id;
    private String name;
    private String manager;
    private long memberCnt;

}

 

 

 

Controller

@RestController
@RequestMapping("/attendance/team")
public class TeamController {
    private final TeamService service;

    public TeamController(TeamService service) {
        this.service = service;
    }

    @GetMapping("/team")
    public List<TeamListResponse> teamList() {
        return service.selectTeamList();
    }
}

 

Service

@Service
public class TeamService {

    private final TeamRepository repository;

    public TeamService(TeamRepository repository) {
        this.repository = repository;
    }

    public List<TeamListResponse> selectTeamList() {
        return repository.findAll().stream()
                .map(team -> new TeamListResponse(team.getName(), 
                team.getManager(), team.getEmployeeCnt()))
                .collect(Collectors.toList());
    }
}
  • JPA에 findAll 메서드를 사용하여 테이블에 저장된 모든 데이터를 가져온다.
  • map()메서드는 TeamListResponse 객체로 변환해준다.
  • collect()메서드를 사용하여 원하는 데이터 타입으로 변환하여 반환해준다.

DTO

@Getter
public class TeamListResponse {
    private String name;
    private String manager;
    private long memberCnt;

    public TeamListResponse(String name, String manager, long memberCnt) {
        this.name = name;
        this.manager = manager;
        this.memberCnt = memberCnt;
    }
}

 

2. 직원

Table

 

Entity

@Entity
@Getter
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    @Column(nullable = false, length = 20)
    private String name;
    @Column(nullable = false, length = 20)
    private String teamName;
    @Column(nullable = false, length = 10)
    @Enumerated(EnumType.STRING)
    private Role role;
    @Column(nullable = false)
    private LocalDate birthday;
    @Column(nullable = false)
    private LocalDate workStartDate;

    protected Employee() {}

    public Employee(String name, String teamName, Role role, 
    				LocalDate birthday, LocalDate workStartDate) {
        this.name = name;
        this.teamName = teamName;
        this.role = role;
        this.birthday = birthday;
        this.workStartDate = workStartDate;
    }
}

 

 

Controller

@RestController
@RequestMapping("/attendance/employee")
public class EmployeeController {

    private final EmployeeService service;

    public EmployeeController(EmployeeService service) {
        this.service = service;
    }

    @PostMapping("/register_employee")
    public void resisterEmployee(@RequestBody ResisterEmployeeRequest request) {
        service.insertEmployee(request);
    }
}

 

Service

@Service
public class EmployeeService {

    private final EmployeeRepository repository;
    private final TeamService teamService;

    public EmployeeService(EmployeeRepository repository, TeamService teamService) {
        this.repository = repository;
        this.teamService = teamService;
    }

    @Transactional
    public void insertEmployee(ResisterEmployeeRequest request) {
    	if (request.getRole() == null) {
            throw new IllegalArgumentException("직급을 입력해주세요.");
        }
        
        repository.findById(request.getId()).ifPresent(employee -> {
            throw new IllegalArgumentException("이미 등록된 직원입니다.");
        });

        repository.save(new Employee(request.getName(), request.getTeamName(),
                request.getRole(), request.getBirthday(), request.getWorkStartDate()));

        if (Role.MANAGER.equals(request.getRole())) {
            teamService.updateManager(request.getTeamName(), request.getName());
        }

        teamService.updateEmployeeCnt(request.getTeamName());
    }
}
  • Role이 null인지 확인하고 없다면 예외를 발생시킨다.
  • Id를 사용하여 이미 등록되어 있는 직원인지 확인한다.
  • 있다면 예외를 발생시키고 없다면 새로운 직원을 만들어준다.
  • Employee 객체를 만들어 새로운 직원을 저장한다.
  • 이때 Role이 매니저인 경우 teamService의 updateManager메서드를 실행시킨다.
  • teamService의 updateEmployeeCnt 메서드를 실행하여 해당 팀의 직원 수를 증가시킨다.

TeamService

@Service
public class TeamService {

    private final TeamRepository repository;

    public TeamService(TeamRepository repository) {
        this.repository = repository;
    }

    @Transactional
    public void updateEmployeeCnt(String teamName) {
        Team team = findTeamByName(teamName);

        team.updateEmployeeCnt();
    }

    @Transactional
    public void updateManager(String teamName, String name) {
        Team team = findTeamByName(teamName);

        team.updateManager(name);
    }

    private Team findTeamByName(String name) {
        return repository.findByName(name)
        	.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 팀 입니다."));
    }
}

 

  • findTeamByName 메서드를 사용하여 존재하는 팀 인지 확인한다.
  • 있다면 각 메서드를 실행하여 DB에 저장해준다.

Repository

public interface EmployeeRepository extends JpaRepository<Employee, Long> {
    Optional<Employee> findById(long id);
}

 

DTO

@Getter
public class ResisterEmployeeRequest {
    private long id;
    private String name;
    private String teamName;
    private Role role;
    private LocalDate birthday;
    private LocalDate workStartDate;
}

 

Controller

@RestController
@RequestMapping("/attendance/employee")
public class EmployeeController {

    private final EmployeeService service;

    public EmployeeController(EmployeeService service) {
        this.service = service;
    }

    @GetMapping("/employee_list")
    public List<EmployeeListResponse> employeeList() {
        return service.selectEmployeeList();
    }
}

 

Service

@Service
public class EmployeeService {

    private final EmployeeRepository repository;
    private final TeamService teamService;

    public EmployeeService(EmployeeRepository repository, TeamService teamService) {
        this.repository = repository;
        this.teamService = teamService;
    }

    public List<EmployeeListResponse> selectEmployeeList() {
        return repository.findAll().stream()
                .map(employee -> new EmployeeListResponse(employee.getName(), 
                	employee.getTeamName(),employee.getRole(), employee.getBirthday(), 
                    employee.getWorkStartDate()))
                .collect(Collectors.toList());
    }
}
  • JPA에 findAll 메서드를 사용하여 테이블에 저장된 모든 데이터를 가져온다.
  • map()메서드는 TeamListResponse 객체로 변환해준다.
  • collect()메서드를 사용하여 원하는 데이터 타입으로 변환하여 반환해준다.

DTO

@Getter
public class EmployeeListResponse {
    private String name;
    private String teamName;
    private Role role;
    private LocalDate birthday;
    private LocalDate workStartDate;

    public EmployeeListResponse(String name, String teamName, Role role, 
    		LocalDate birthday, LocalDate workStartDate) {
        this.name = name;
        this.teamName = teamName;
        this.role = role;
        this.birthday = birthday;
        this.workStartDate = workStartDate;
    }
}

 

github - https://github.com/ddonydev/inflearn_study

 

[정리]

JPA를 다뤄본지 얼마 되지 않아 조금 어려웠고,

계층을 어떻게 나누어야할지 모르겠어서 그냥 강사님이 강의해서 해주신 그대로 만들어 보았다.

 

직원을 등록할때 Role이 Manager인 경우 팀 테이블에 있는 Manager 컬럼에 해당 직원의 이름을 넣어주고,

등록할때 마다 직원의 수를 update해주고 싶었는데 이를 어떻게 해결해야할지 이틀을 고민하였다.

 

그래서 생각한 것이 직원을 구현하고 있는 Service에 팀 Service를 주입 받아 사용하는 방법 밖에 떠오르지 않았고,

이게 맞는 것인지 다시 고민에 빠졌다.

 

"Service에 다른 도메인 Service 호출"이라는 키워드로 검색해 보았고, 의견은 많이 갈리는 것 같았다.

Controller에 여러 서비스를 호출하여 사용하는 방법, Service는 무조건 DAO와의 의존 관계를 갖게 하는 등 여러가지 방법이 있었다.

 

하지만 나는 JPA로 사용했기 때문에 DAO에 해당하는 Mapper는 사용하지 못할 것이라고 판단하였고,

Controller에서 여러 서비스를 호출하면 트랜잭션 문제가 생긴다는 글을 보았다.

 

그래서 결국 내가 처음 생각한 Service에 팀 Service를 주입 받아 사용하는 방법을 사용하여

내가 원하던 기능을 구현할 수 있었다.

 

이게 맞는 것인지 잘 모르겠고 아직 트랜잭션에 대한 개념이 확실하지 않아 조금 더 공부한 뒤

만들어본 코드를 다듬어 보아야겠다.

 

잘못된 부분 부족한 부분이 있다면 편하게 댓글로 남겨주세요☺️

 

 

강의 링크 👉 자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]

728x90

+ Recent posts