1. 프로젝트 진행 상황 및 계획
🥇 팔로잉 CRUD 리팩토링 (refactoring) (완료)
🥈 전체 리팩토링 (진행 중, 24.12.26 아침 완료 목표)
🥉 사용자 CRUD 중 U에서 발생할 수 있는 예외 처리하기 (진행 중, 24.12.26 아침 완료 목표)
4️⃣ 오늘 느낀 점과 고민을 TIL에 기록하기 (완료)
5️⃣ 뉴스피드 프로젝트 중 발생한 트러블슈팅(troubleshooting) 초안 수정하기 (진행 중, 24.12.26 아침 완료 목표)
6️⃣ 24.12.20 TIL 내용 보충하기 (진행 중, 24.12.26 저녁 완료 목표)
7️⃣ 일정 관리 앱 Develop 밀린 트러블슈팅 작성 끝내기 (보류, 24.12.29 완료 목표)
8️⃣ 일정 관리 앱 만들기 KPT 회고하기 (보류, 24.12.29 완료 목표)
9️⃣ 일정 관리 앱 Develop KPT 회고하기 (보류, 24.12.29 완료 목표)
2. 고민
Q1. 자기 자신을 팔로잉하려는지 검증해야 하지 않을까?
A1. 팀에서 현재 구현 중인 애플리케이션(application)은 회원 한 명이 프로필(profile)을 여러 개 생성할 수 있는데, 데이터베이스(database)에 정보가 잘 저장되었는지 확인하다가 문득 의문이 들었다. 팔로잉하는 사람이 많은 척 누군가 자신의 프로필을 다른 사람인 척 팔로잉한다면, 이 또한 막아야 좋을 듯했다. 이유는 간단하고도 명확했다. 팔로잉 수에 따라 광고나 협찬의 규모가 달라질 테니까. 다른 사람인 척 자기 프로필을 팔로잉하는 행위 또한 일종의 부정행위이므로 이를 막을 추가 검증이 필요하다고 생각했다.
package com.spring.instafeed.follower.service;
import com.spring.instafeed.base.Status;
import com.spring.instafeed.follower.dto.response.CreateFollowerResponseDto;
import com.spring.instafeed.follower.entity.Follower;
import com.spring.instafeed.follower.repository.FollowerRepository;
import com.spring.instafeed.profile.entity.Profile;
import com.spring.instafeed.profile.repository.ProfileRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class FollowerServiceImpl implements FollowerService {
// 속성
private final FollowerRepository followerRepository;
private final ProfileRepository profileRepository;
@Transactional
@Override
public CreateFollowerResponseDto createFollower(
Long senderProfileId,
Long receiverProfileId
) {
Profile senderProfile = profileRepository
.findById(senderProfileId)
.orElseThrow(
() -> new ResponseStatusException(
HttpStatus.NOT_FOUND,
"Id does not exist"
)
);
Profile receiverProfile = profileRepository
.findById(receiverProfileId)
.orElseThrow(
() -> new ResponseStatusException(
HttpStatus.NOT_FOUND,
"Id does not exist"
)
);
// --- 자기 자신을 팔로잉하는지 검증하는 구간 시작 ---
Long senderUserId = senderProfile
.getUser()
.getId();
Long receiverUserId = receiverProfile
.getUser()
.getId();
if (senderUserId.equals(receiverUserId)) {
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST,
"Cannot follow your own profile"
);
}
// --- 자기 자신을 팔로잉하는지 검증하는 구간 종료 ---
Follower followerToSave = Follower.create(
senderProfile,
receiverProfile,
Status.PENDING
);
Follower savedFollower = followerRepository.save(followerToSave);
return CreateFollowerResponseDto.toDto(savedFollower);
}
}
자기 자신을 팔로잉하는지 검증하는 구간을 추가한 다음, 사진처럼 데이터베이스에 임시로 정보를 저장한 다음 애플리케이션을 실행했다.
예외 처리는 목요일에 팀에서 한꺼번에 진행할 예정이었으므로, 작성한 검증 구간에서 오류가 발생하지 않는지 확인했다. 다행히 별다른 오류는 없었다.
프로필을 생성한 사용자가 서로 다를 때 팔로잉을 요청하는 기능 또한 문제없었다.
Q2. 프로필 A가 프로필 B에게 팔로잉을 요청했는데, 나중에 프로필 B이 프로필 A에게 또 요청할 필요가 있나?
A2. 기능 구현과 리팩토링(refactoring), 확인을 거듭하다가 데이터베이스를 본 순간 두 번째 물음표가 머리 위로 떠올랐다. 사진처럼 반대로 팔로잉을 요청했을 때 예외 처리를 하지 않는다면, 이는 사용자가 불필요한 요청을 하도록 놔두는 모양새가 되었다. 이 부분 또한 추가로 검증해야 맞다고 생각했다. 나도 중복을 싫어하는데, 내가 싫어하는 일을 다른 사람에게 시킬 순 없지 않은가.
package com.spring.instafeed.follower.service;
import com.spring.instafeed.base.Status;
import com.spring.instafeed.follower.dto.response.CreateFollowerResponseDto;
import com.spring.instafeed.follower.entity.Follower;
import com.spring.instafeed.follower.repository.FollowerRepository;
import com.spring.instafeed.profile.entity.Profile;
import com.spring.instafeed.profile.repository.ProfileRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class FollowerServiceImpl implements FollowerService {
// 속성
private final FollowerRepository followerRepository;
private final ProfileRepository profileRepository;
@Transactional
@Override
public CreateFollowerResponseDto createFollower(
Long senderProfileId,
Long receiverProfileId
) {
Profile senderProfile = profileRepository
.findById(senderProfileId)
.orElseThrow(
() -> new ResponseStatusException(
HttpStatus.NOT_FOUND,
"Id does not exist"
)
);
Profile receiverProfile = profileRepository
.findById(receiverProfileId)
.orElseThrow(
() -> new ResponseStatusException(
HttpStatus.NOT_FOUND,
"Id does not exist"
)
);
Long senderUserId = senderProfile
.getUser()
.getId();
Long receiverUserId = receiverProfile
.getUser()
.getId();
if (senderUserId.equals(receiverUserId)) {
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST,
"Cannot follow your own profile"
);
}
// --- 동일한 팔로잉 요청이 있는지 검증하는 구간 시작 ---
followerRepository
.findBySenderProfileIdAndReceiverProfileId(
senderProfileId,
receiverProfileId
)
.ifPresent(follower -> {
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST,
"The requested data already exists"
);
}
);
// --- 동일한 팔로잉 요청이 있는지 검증하는 구간 시작 ---
// --- 반대로 팔로잉 요청이 있는지 검증하는 구간 시작 ---
followerRepository
.findBySenderProfileIdAndReceiverProfileId(
receiverProfileId,
senderProfileId
)
.ifPresent(reverseFollower -> {
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST,
"The reverse requested data already exists"
);
}
);
// --- 반대로 팔로잉 요청이 있는지 검증하는 구간 종료 ---
Follower followerToSave = Follower.create(
senderProfile,
receiverProfile,
Status.PENDING
);
Follower savedFollower = followerRepository.save(followerToSave);
return CreateFollowerResponseDto.toDto(savedFollower);
}
}
두 번째로 추가한 검증 구간에 문제가 없는지는 데이터베이스에 팔로잉 요청을 하나 저장한 다음에 확인했다.
우선 반대로 팔로잉 요청이 왔을 때 의도한 대로 작성한 메시지가 나오는지 확인했다. 팔로잉 요청 자체는 관점에 따라 Bad Request, 즉 잘못된 요청이 아니므로 메시지와 Http 상태 코드는 팀에서 논의 후 바뀔 수 있다.
그다음에 확인한 '동일한 요청이 이미 있다'는 검증도 문제없이 이루어졌다.
3. 회고
"저희는 여력이 된다면 도전 단계에 도전하겠지만, 일단은 일정을 짤 때 도전 단계는 고려하지 않겠습니다. 팀원 한두 명에게 도전 단계를 구현해달라고 하면 할 순 있겠지만, 그게 팀 전체에 도움은 되지 않는다고 생각하거든요. 혹시 필수 단계만 구현해서 아쉽진 않으신가요?"
팀 구성원으로 넷이 모인 첫날, 가장 먼저 도전 단계 과제를 포기했고 팀원 모두 이에 동의했다. 휴일을 맞이하기 전날 팀원 한 명씩 말을 걸어 필수 단계만 해서 아쉽진 않은지 물었다. 다행이라고 해야 할까. 아무도 딱히 아쉽지 않다고 대답했다. 주변에서는 꽤 처음부터 도전 단계까지 포함해서 일정을 짰는데, 팀장으로서 도전 단계까지 일주일이 채 안 되는 시간에 해낼 자신이 없었다. 정확히는 그렇게 팀을 몰아붙여서 해낸들 의미가 있을지 의구심이 크게 들었다. 혼자 욕심내면 과연 좋은 팀장이라고 할 수 있을까.
문득 '좋은 팀장은 어떤 사람일까?'라는 질문이 머릿속의 잡념을 모두 밀어냈다. 리팩토링(Refactoring)해야 할 부분이 끝도 없이 생기지만, 그래도 시간을 내어 이 질문을 곰곰이 곱씹었다. 나름 몇 년 동안 직장인으로, 강사로 일하면서 만난 사수와 팀장의 모습이 주마등처럼 스쳐 지나갔다.
그러게, 좋은 팀장은 대체 어떤 사람일까?
팀원을 잘 챙기는 사람?
좋다.
인사 한마디에도 다정함과 예의가 묻어나는 사람?
마찬가지로 좋다.
다 좋은데, 이런 점은 꼭 팀장이 아니어도 기본으로 갖춰야 할 태도 아닌가?
정답은 아닐지언정 적어도 구색을 갖춘 대답을 하려면, 여태 보낸 나날 속에서 어떤 사람을 보고 '나도 저런 사람이 되고 싶다'고 느꼈는지 되짚어야 했다.
'같은 팀이 되면 뭐라도 하나 배울 수 있겠다 싶은 사람'
'팀의 배움과 성장을 가장 중요한 목표로 생각하는 사람'
모든 기능을 구현하고 싶다는 마음보단 이번 프로젝트가 팀원 모두에게 배움의 기회가 되길 바랐으니, 우선 저렇게 정의해야겠다.
잠깐만, 결과물의 완성도도 놓치면 안 되는데?
역시, 좋은 팀장이 되려면 갈 길이 구만리였다.
'끝을 보는 용기' 카테고리의 다른 글
Day 082 - 뉴스피드 프로젝트 100%, 아니 협업에 문제가 없었다고 적어요? 진짜로? (0) | 2024.12.27 |
---|---|
Day 081 - 뉴스피드 프로젝트 90%, 발표회까지 남은 시간은 단 하루 (0) | 2024.12.26 |
Day 079 - 뉴스피드 프로젝트 65%, 팀장에게 인후염은 숙명이다. (0) | 2024.12.24 |
Day 078 - 뉴스피드 프로젝트 50%, 레코드 클래스(Record Class)를 처음 쓰다, 세터(Setter) 잡으러 다니느라 바쁜 사람? 저요! (0) | 2024.12.23 |
Day 077 - 뉴스피드 프로젝트 15%, Octotree 사용하기, 깃허브(GitHub)에서 난생처음 상어 뱃지를 얻다. (0) | 2024.12.22 |