끝을 보는 용기

Day 077 - 뉴스피드 프로젝트 15%, Octotree 사용하기, 깃허브(GitHub)에서 난생처음 상어 뱃지를 얻다.

writingforever162 2024. 12. 22. 23:57

[인용 및 참고 출처]

1. 구글 검색: 브런치, "팀장은 외롭다", 팀장님이 나에게 외롭다고 말했다, (2024.12.22)

 

1. 프로젝트 진행 상황 및 계획 

🥇 뉴스피드 프로젝트 진행하기: 회원 정보 조회 (완료)
🥈 뉴스피드 프로젝트 진행하기: 본인 비밀번호 수정 (완료)
🥉 뉴스피드 프로젝트 진행하기: 회원 탈퇴 (완료)
4️⃣ 밀린 트러블슈팅(troubleshooting) 작성 끝내기
5️⃣ 일정 관리 앱 만들기 KPT 회고하기
6️⃣ 일정 관리 앱 Develop KPT 회고하기
7️⃣ 뉴스피드 프로젝트 진행 후 느낀 점 TIL에 정리하기 (완료)

 

2. 고민

Q1. 스프링 시큐리티(Spring Security)를 사용할까 말까?

A1. 스프링 시큐리티를 사용했다. JWT(Json Web Token)을 구현하는 데에 스프링 시큐리티를 사용하진 않고, 스프링 시큐리티가 지원하는 암호화 기능을 사용하여 비밀번호를 암호화했다. 기존에 사용한 Bcrypt 대신 스프링 시큐리티를 사용한 셈인데, 우선 코드가 훨씬 깔끔해졌다는 점이 이런 결정을 내린 첫 번째 이유였다. 두 번째 이유로는 스프링 시큐리티가 최신 보안 관행을 따르도록 설계되어서 보안 측면에서 더 낫다고 판단이 되었다. 마지막으로는 팀에서 애초에 '스프링 시큐리티를 나중에라도 적용하자'는 방향으로 프로젝트를 계획했기 때문에 유지보수 측면에서도 통합이 더 잘될 거라고 생했다.

 

Q2. 회원이 비밀번호를 수정했을 때 Response DTO에는 어떤 정보가 담겨야 할까?

더보기
package com.spring.instafeed.dto.user.response;

import com.spring.instafeed.User;
import lombok.Getter;

// [1/3] response DTO에 해당하는 클래스(class)
@Getter
public class UserResponseDto {
    private final Long id;
    private final String name;
    private final String email;

    public UserResponseDto(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    public static UserResponseDto toDto(User user) {
        return new UserResponseDto(user.getId(), user.getName(), user.getEmail());
    }
}
package com.spring.instafeed.controller;

import com.spring.instafeed.dto.user.request.UpdateUserRequestDto;
import com.spring.instafeed.dto.user.response.UserResponseDto;
import com.spring.instafeed.service.UserServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

// [2/3] Presentation Layer
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
    private final UserServiceImpl userService;

    @PatchMapping("/{id}")
    public ResponseEntity<UserResponseDto> updatePasswordById(
            @PathVariable Long id,
            @RequestBody UpdateUserRequestDto requestDto
    ) {
        UserResponseDto responseDto = userService.updatePasswordById(id, requestDto.getPassword());

        return new ResponseEntity<>(responseDto, HttpStatus.OK);
    }
}
package com.spring.instafeed.service;

import com.spring.instafeed.User;
import com.spring.instafeed.dto.user.response.UserResponseDto;
import com.spring.instafeed.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

// [3/3] Service Layer
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
    private final UserRepository userRepository;

    @Transactional
    @Override
    public UserResponseDto updatePasswordById(Long id, String password) {
        User foundUser = userRepository.findByIdOrElseThrow(id);

        foundUser.update(password);

        return UserResponseDto.toDto(foundUser);
    }
}

A2. Postman을 켜서 프로그램을 실행했을 때 오류가 발생하진 않았으나, 비밀번호를 수정했을 때 '이메일과 본명을 클라이언트(client)에게 응답으로 전달해야 하는가?'라는 의문이 문득 들었다. 말 그대로 '굳이?'라는 의문이 머릿속을 맴돌았다. 클라이언트에게 필요한 정보는 '비밀번호가 잘 수정되었다'는 한마디로 충분해 보였다. 이런 이유로 기존에 사용한 UserResponseDto 대신 새로운 Response Dto 클래스(class)를 생성한 다음 코드를 수정했다.

더보기
package com.spring.instafeed.dto.user.response;

import lombok.Getter;

// [1/3] 비밀번호 수정 요청의 응답에 따른 Response DTO
@Getter
public class UpdateUserResponseDto {
    private final String success;

    public UpdateUserResponseDto(String success) {
        this.success = success;
    }
    // 응답 메시지가 변경될 수 있기 때문에 초기화하지 않음
}
package com.spring.instafeed.service;

import com.spring.instafeed.User;
import com.spring.instafeed.dto.user.response.UpdateUserResponseDto;
import com.spring.instafeed.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

// [2/3] Service Layer
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
    private final UserRepository userRepository;

    @Transactional
    @Override
    public UpdateUserResponseDto updatePasswordById(Long id, String password) {
        User foundUser = userRepository.findByIdOrElseThrow(id);

        foundUser.update(password);

        String success = "비밀번호 수정에 성공했습니다.";

        return new UpdateUserResponseDto(success);
    }
}
package com.spring.instafeed.controller;

import com.spring.instafeed.dto.user.request.UpdateUserRequestDto;
import com.spring.instafeed.dto.user.response.UpdateUserResponseDto;
import com.spring.instafeed.service.UserServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

// [3/3] Presentation Layer
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
    private final UserServiceImpl userService;

    @PatchMapping("/{id}")
    public ResponseEntity<UpdateUserResponseDto> updatePasswordById(
            @PathVariable Long id,
            @RequestBody UpdateUserRequestDto requestDto
    ) {
        UpdateUserResponseDto responseDto = userService.updatePasswordById(id, requestDto.getPassword());

        return new ResponseEntity<>(responseDto, HttpStatus.OK);
    }
}

자칫 Postman에서 200 OK 메시지가 나왔다고 데이터베이스(database) 확인을 깜빡할 수 있기 때문에 놓치지 말자는 의미로 데이터베이스에 정보가 잘 저장된 모습을 함께 캡처했다. 

 

3. 깃허브(GitHub)에서 편하게 파일을 확인하고 싶을 때 쓰는 Octotree 사용하기 (Chrome 확장 프로그램)

(1) 크롬 오른쪽 위에 있는 더보기 버튼(점 세 개) → 확장 프로그램 → Chrome 웹 스토어 방문하기 차례대로 클릭하기 ▼

(2) 'Octotree' 검색 후 'Octotree - GitHub code tree'를 크롬에 추가하기

(3) 크롬 오른쪽 위에 있는 더보기 버튼(점 세 개) → 확장 프로그램 → 확장 프로그램 관리 차례대로 클릭 후 확장 프로그램의 활성화 여부 확인하기 ▼

(4) Octotree - GitHub code tree 사용 시 모습

4. 회고 

주말에 모든 팀원에게 연락해야 할 일이 생겼다. 처음에는 레이어(layer)별로 패키지(package)를 나누었는데, 막상 깃허브(GitHub)에서 합친 다음 확인하니 기능은 동일한데 이름만 다른 클래스(class)도 많고 무언가를 수정하려고 하면 어느 패키지를 열어야 하는지 한눈에 들어오지 않아 패키지 구성을 통째로 바꾸어야 했다. 주말에 젭(ZEP)에 들어온 팀원이랑 진행하고 팀 전체에 통보식으로 공유하면 당장 작업하기엔 수월할지 모르나, 본인이 생성한 파일이 갑자기 사라져 버리면 더 큰 혼란을 불러일으킬 수 있었기에 고민 끝에 팀원들에게 문자를 보냈다.

 

'황금 같은 주말 오밤중에 연락하는 팀장이라니. 다면평가 점수가 어떻게 나올지 너무 기대된다, 정말!'

 

다 같이 모여 논의해야 할 사항은 뻥튀기인 양 불어나고, 시간이 흐를수록 마음은 덩달아 조급해지고, 이 와중에 수요일은 양심에 털 나지 않은 이상 건드려선 안 되는 크리스마스이고, 개복치 못지않은 목을 더 썼다가는 제대로 인후염에 걸릴 판인데 이번 주는 절대 아프면 안 되는 시기이고, 군말 없이 달려와서 함께 프로젝트를 진행하는 팀원들에게는 늦은 시간에 불러내서 미안할 따름이고…….

 

'어떤 팀장이 되어야 최상의 결과물을 만들 수 있을까?'

 

'어떻게 행동해야 누구 하나 감정 상하는 일이 없을까?'

 

문득 회사에 다닐 적에 종종 들은 '팀장은 외로운 자리'라는 한마디가 떠올랐다. 회사의 팀장은 아니지만, 오늘 유독 그 말이 공감됐다. 체력이든 마음이든 힘들다 한들 팀원들이나 주변에 말할 수 없는 사람이라고 생각하기 때문이다. 팀원들에게 얘기하자니 사기가 꺾일 듯싶고, 주변에 얘기하면 팀의 얼굴에 먹칠하는 팀장이 될 테니까. 지금 팀장으로서 말 못 할 고민은 없으나, 왠지 모르게 그 외로움이 무엇인지 알 듯했다.

 

오늘 내심 아쉬운 마음을 달래며 책을 반납한 다음, 신착 도서 목록을 본 순간 운명처럼 책 한 권을 마주쳤다. 개발 분야 도서는 아니지만, 앞으로 남은 팀 프로젝트를 생각한다면 무조건 읽어야 할 책 같았다. 개인 프로젝트를 포함하여 팀장 맡은 횟수만 벌써 세 번인지라, 제목에 '팀장'이라는 두 글자가 떡하니 박힌 책을 반드시 읽어야겠다 싶었다.

 

블로그에 개발 분야 책 외에 다른 책 내용을 적을 일은 없을 줄 알았는데, 사람 일은 정말 알다가도 모르겠다.

 

월요일에는 오늘보다 좀 더 나은 팀장이 되어, 팀원 모두가 휴일을 오롯이 휴일답게 보낼 수 있도록 해야겠다.

 

[24.12.23 추가] 오늘의 기념사진. 뱃지에 그려진 상어는 생각보다 훨씬 귀여웠다.