FE/BE 코드 규칙 설정
공통 사항
- 모델 객체 필드 값은 디폴트 값 없이 사용하는 것을 원칙으로 한다. (데이터 value를 의도적으로 제한하기 위함)
- 디폴트를 제거하는 다른 이유 : 부득이 null값이 들어가야할 때가 있다. 그럴 때 null값을 다른 값으로 디폴트 처리하면 코드 작업이 더 딜레이 될 것으로 생각하여 판단 함.
FE 공통 규칙
프론트에 생성하는 객체는 데이터를 주고 받는 그릇으로 활용하기 위해 생성. 공통으로 사용하는 함수를 개발해야 api개발에 사용할 때 편의성을 증대 시킬 수 있다. 유지보수성을 높히기 위한 이유도 포함.
Model
- 소문자 camelCase로 변수명을 작성한다.
- private으로 외부에서 접근 못하도록 설정한다.
- PModel의 함수에 따르고 IModel에서 상속 받는 추상 함수는 재정의 할 때 타입을 준수한 범위 내에서 모든 코드 구조는 변경 가능하다.
- 디렉토리는 src/apis/해당클래스명/index.ts 로 작성한다. type 설정이 많아지면 index.ts와 같은 위치에 types.ts를 생성하여 모듈화한다.
- export는 default가 아닌 객체로 내보내는 것을 기본으로 한다.
- 디렉토리에서 기능별로 나누려면 src/apis/기능명/index.ts 로 작성한다.
- 공통의 api는 src/utils/index.ts 에 작성한다. 공통의 type은 src/apis/commonTypes.ts에 정의한다.
IModel의 개념
interface Model을 의미한다.
interface로서 모든 클래스에 필요한 공통의 함수명과 인자, 반환 값을 지정한다.
PModel의 개념
extends 하여 사용하기 위한 클래스로 공통으로 타입에 관계없이 사용할 수 있도록 확장하여 활용하기 위한 목적으로 만들어졌다.
// IModel과 PModel을 상속한 예시 코드
class User extends PModel implements IModel {
private nickName: ModelStringValue;
//...
public set(column: UserColumn, value: ModelValue) {
// ...
}
}
API
api는 아래 규칙을 따른다.
- api 작업 시 #41 의 내용을 토대로 생성하며, 수정 사항 발생 시 정리하여 #41 에 추가로 코멘트를 생성한다.
- 수정사항은 백엔드 restController와 충분히 테스트 및 검토 후 반영한다.
- 함수 명칭은 MongoRepository 기준으로 하며 예시는 다음과 같다.
const findUserAll () { /* ... */ }
const findUserById (id: string) { /* ... */ }
const findDiaryByUid (uid: string) { /* ... */ }
const insertUser (user: User) { /* ... */ }
const updateUser (user: User) {
const formData = user.makeFormData();
return axios.put('/api/user', formData)
.then(handleReceiveData)
.catch(handleReceiveError);
}
const deleteUserById (id: string) { /* ... */ }
BE 공통 규칙
Entity 클래스
- 소문자 camelCase로 변수명을 작성한다.
- private으로 외부에서 접근 못하도록 설정한다.
- update가 필요한 클래스에 한해서 Entity 클래스를 확장해서 replaceIfNotNull을 사용한다.
- 객체 update에 필요한 함수 replaceIfNotNull 내용은 아래와 같다.
public class Entity<T> {
public T replaceIfNotNull(T compare) {
List<Field> fields = Arrays.asList(this.getClass().getDeclaredFields());
fields.forEach(field -> {
try {
field.setAccessible(true);
Object compareField = field.get(compare);
Object thisField = field.get(this);
if (field.getName() == "userAuth") {
field.set(this, "USER");
} else {
field.set(this, compareField != null ? compareField : thisField);
}
} catch (IllegalAccessException e) {
System.out.println("The value is null [" + field.getName() + "]");
}
});
return (T) this;
}
}
// 사용 예시
public class User extends Entity<User> implements UserDetails {
// ...
}
Repository
-
리파지토리 명칭은 ClassNameRepository로 한다.
-
config디렉토리 MongoAuditConfig에 지정해두었기 때문에 @repository는 달지 않는다.
-
ClassNameRepository : interface
-
ClassNameRepositoryCustom : interface - 커스터마이징 구현 함수 설정
-
ClassNameRepositoryCustomImpl : class - 커스터마이징 구현 함수 작성 (MongoTemplate를 생성자 주입으로 사용하는 것을 원칙으로 한다.)
기본적으로 단순한 findAll, findById, insert, deleteById는 ClassNameRepository에서 사용하고, 쿼리문이 필요한 함수만 커스터마이징하여 사용한다.
Service
- restController를 무겁게 하는 것을 피하기 위해 Service는 필수로 사용한다.
- Service에서 해당하는 Repository를 생성자 주입으로 사용하는 것을 원칙으로 한다.
함수 명칭은 ClassNameRepositoryCustomImpl, ClassNameService, ClassNameRestController 모두 동일하게 처리한다. 예를 들면 다음과 같다.
// DiaryRepositoryCustomImpl
class DiaryRepositoryCustomImpl implements DiaryRepositoryCustom {
private MongoTemplate diaryTemplate;
@Autowired
DiaryRepositoryCustomImpl(MongoTemplate diaryTemplate) {
this.diaryTemplate = diaryTemplate;
}
public Optional<User> findByUid (String uid) {
Criteria cr = new Criteria("uid").is(uid);
Query q = new Query(cr);
return diaryTemplate.findOne(q, Diary.class);
}
}
// DiaryService
@Service
class DiaryService {
private DiaryRepository diaryRepository;
@Autowired
DiaryRepositoryCustomImpl(DiaryRepository diaryRepository) {
this.diaryRepository= diaryRepository;
}
public User findByUid (String uid) {
return diaryRepository.findByUid(uid).orElseThrow();
}
}
// DiaryRestController
@RestController
@RequestMapping("/api")
class DiaryRestController {
private DiaryService diaryService;
@Autowired
DiaryRepositoryCustomImpl(DiaryService diaryService) {
this.diaryService= diaryService;
}
private String mapper(Object object) throws JsonProcessingException {
return new ObjectMapper().writeValueAsString(object);
}
@GetMapping("/user/uid/{uid}")
public String findByUid (@PathVariable("uid") String uid) throws JsonProcessingException {
return mapper(diaryService.findByUid(uid));
}
}