프로젝트 기획 단계에서는 봉사활동을 카테고리 별로 나눈 후, 카테고리로 검색할 수 있는 기능을 만드는 수준에서 끝이 났었다. 하지만 게시물에 해시태그와 같은 기능을 추가하면 어떨까 라는 아이디어가 프로젝트 진행 중에 나오게 되었다.
처음에는 인스타그램과 같이 해시태그를 커스텀해서 만드는 형태를 생각했었지만 이렇게 커스텀할 경우, 띄어쓰기, 오타, 다양한 단어 사용 등의 이유로 해시태그들이 통일성이 떨어질 것이 우려되었다. 그래서 결국 해시태그 여러개를 지정해 놓고 그 안에서 고르는 형태로 진행하게 되었다.
여러모로 이 방법이 좋다고 생각하는게 앞서 언급했듯이 통일성이 생기기 때문에 한 해시태그에 해당하는 게시글을 검색하기가 수월하고, db에도 지정된 해시태그들만 저장되어 복잡해지지 않을거라 생각했다.
String hashtag = "#CHILD#CLEANING..."
board 테이블에 문자열 형태의 컬럼을 만들고 해시태그 여러개를 한 문자열에 이어 붙여서 저장하는 형태를 생각해 보았다. 이렇게
하면 데이터를 경제적으로 사용할 수 있다는 점이 장점이지만 해시태그 별 검색을 하기에는 매우 어려운 구조이다.
List<String> = ["CHILD", "CLEANING",...]
이렇게 저장하는 방식은 문자열 형태로 저장하는 것보다는 검색하기에 수월하겠지만 그래도 여전히 어려운 구조이다. 그리고 한 컬럼 내에 여러 개의 데이터를 저장하는 방식이 좋은 방법은 아니라고 한다.
내 지식으로는 검색하기에 가장 수월한 방법이 테이블을 새로 만드는 방법밖에는 없다고 생각한다. 이렇게 테이블을 새로 만들면 게시물을 조회할 때마다 게시물 테이블과 해시태그 테이블을 모두 조회해와야 한다는 단점이 있긴 하지만 이 이슈는 해시태그의 최대 개수를 제한하는 방법을 통해 성능을 그나마 유지할 수 있을 것이다. 그 대신, 테이블을 새로 만듦으로써 장점은 게시물 별 해시태그 조회는 당연 가능하고, 해시태그별 검색도 할 수 있게 된다.
@Transactional
public ResponseDto<MsgResponse> updateMyProfile(Member member, CompanyUpdateRequest companyUpdateRequest) throws IOException {
check.isAdmin(member);
check.isPassword(companyUpdateRequest.getPassword(), companyUpdateRequest.getPasswordConfirm());
String profileImage = s3Uploader.uploadFiles(companyUpdateRequest.getProfileImage(), "company");
member.update(companyUpdateRequest, profileImage);
return ResponseDto.success(new MsgResponse("수정 완료!"));
}
위 코드는 버그를 발견했을 당시의 코드이다. 코드 리뷰를 짧게 하자면, 매개변수로 받은 member 객체를 수정 요청한 데이터로 update를 한 후, flush를 통해 db에 저장하도록 구현을 했다. 이렇게 코드를 구현하니까 수정된 데이터가 db에 적용이 안되는 이슈가 발생했다.
여기서 문제는 매개변수로 받은 멤버 객체에서 치명적인 오류가 있었다. 매개변수로 받은 맴버 객체는 Spring Security에서 제공하는 @AuthenticationPrincipal을 적용한 UserdetailImpl로부터 불러온 멤버 객체이다. 단순히 추측일뿐이긴 하지만 JPA가 제공하는 멤버 객체가 아니라서 db에 반영이 안되는 거라고 생각해서 JpaRepository로부터 불러온 멤버 객체를 업데이트하니까 db에 잘 적용이 되었다. 이 이슈 관련해서는 더 공부해볼 필요가 있을 것 같다.
위와 같이 이슈를 해결하고 난 후, 수정한 비밀번호로 재로그인을 시도했는데 401 UnAuthorized 에러가 발생했다. 비밀번호가 틀렸다는 의미이다. 비밀번호가 잘 수정되었는지 테이블을 확인해보았는데 입력받은 문자열 그대로 저장이 되어있었다.
이 이슈는 매우 위험한 이슈이다. 개발적으로 위험한 수준이 아니라 철컹철컹할 수도(?) 있는 수준의 위험성이다. 현재는 개발단계이고 우리 웹서비스를 이용하는 사람도 없으니 문제될 것이 없지만 나중에 웹서비스를 배포하게 되고, 사용자가 생길 때 이런 이슈가 터지게 되면 보안 관련 법안에 저촉되는 행위라는 이야기를 들었다.
등골이 오싹해지면서 곧바로 코드를 수정하여 요청받은 비밀번호 문자열을 인코딩하여 db에 update시킴으로써 해결하였다.
프로필 수정 관련 이슈를 모두 해결한 후의 코드이다.
@Transactional
public ResponseDto<MsgResponse> updateMyProfile(Member member, CompanyUpdateRequest companyUpdateRequest) throws IOException {
check.isAdmin(member);
// 시큐리티에서 제공하는 member 객체에 db 로부터 불러온 member 객체 덮어 쓰기
member = check.findMember(member.getMemberId());
check.isPassword(companyUpdateRequest.getPassword(), companyUpdateRequest.getPasswordConfirm());
String encodedPassword = passwordEncoder.encode(companyUpdateRequest.getPassword());
String profileImage = s3Uploader.uploadFiles(companyUpdateRequest.getProfileImage(), "company");
member.update(companyUpdateRequest, profileImage, encodedPassword);
return ResponseDto.success(new MsgResponse("수정 완료!"));
}