1. View

uploadForm.mustache
<h4 class="card-title">보유 스킬</h4>
<table class="table">
<tr>
<td>
<div class=form-control style="display: flex; justify-content: space-evenly">
<input type="checkbox" name="skill" value="java">
<span>java</span>
<input type="checkbox" name="skill" value="javaScript">
<span>javaScript</span>
<input type="checkbox" name="skill" value="Spring">
<span>Spring</span>
<input type="checkbox" name="skill" value="HTML">
<span>HTML</span>
<input type="checkbox" name="skill" value="jQuery">
<span>jQuery</span>
<input type="checkbox" name="skill" value="MySQL">
<span>MySQL</span>
<input type="checkbox" name="skill" value="없음">
<span>없음</span>
</div>
</td>
</tr>
2. 스킬 테이블
Skill
@Entity
@Data
@Table(name = "skill_tb")
public class Skill { // Tech Stack
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private Integer userId; // null 허용 -> 5
private Integer employerId; // 1 -> null 채용공고로 옮김.
private String skillName;
private Integer boardId ; // 채용공고 id
private Integer resumeId;
}
3. DTO
import java.util.List;
public class ResumeRequest {
@Data
public class UploadDTO {
// title, cv, schoolName, major, educationLevel, career
private String title;
private String content;
private String schoolName;
private String major;
private String educationLevel;
private String career;
private List<String> skill;
}
체크박스에서 name="skill" 으로 받은 데이터를 List 타입으로 받음.
4. 컨트롤러
SkillController
@PostMapping("/resume/upload")
public String upload(ResumeRequest.UploadDTO requestDTO) {
User sessionUser = (User) session.getAttribute("sessionUser");
Integer resumeId = resumeRepository.upload(requestDTO, sessionUser.getId());
List<String> skills = requestDTO.getSkill();
for (String skill : skills) {
skillRepository.uploadByUserId(skill, sessionUser.getId(), resumeId);
}
return "redirect:/user/" + sessionUser.getId();
}
이력서를 작성하면서 기술 테이블에 데이터를 INSERT.
skill 은 List 타입이기 때문에 반복문으로 하나씩 데이터를 추가한다.
5. 레파지토리
ResumeRepository
@Transactional
public Integer upload(ResumeRequest.UploadDTO requestDTO, Integer userId) {
String q = """
INSERT INTO resume_tb
(user_id, title, content, school_name, major, education_level, career, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, now())
""";
Query query = entityManager.createNativeQuery(q);
query.setParameter(1, userId);
query.setParameter(2, requestDTO.getTitle());
query.setParameter(3, requestDTO.getContent());
query.setParameter(4, requestDTO.getSchoolName());
query.setParameter(5, requestDTO.getMajor());
query.setParameter(6, requestDTO.getEducationLevel());
query.setParameter(7, requestDTO.getCareer());
query.executeUpdate();
String q1 = """
select max(id) from resume_tb;
""";
Query query1 = entityManager.createNativeQuery(q1);
Integer resumeId = (Integer) query1.getSingleResult();
return resumeId ;
}
이력서 INSERT. Resume_tb 의 ID 조회를 위해 INSERT 후 MAX(ID) 를 조회한다.
SkillRepository
@Transactional
public void uploadByUserId(String skill, int userId,int resumeId) {
String q = """
INSERT INTO skill_tb(user_id, skill_name,resume_id) VALUES (?, ?,?)
""";
Query query = entityManager.createNativeQuery(q);
query.setParameter(1,userId);
query.setParameter(2,skill);
query.setParameter(3,resumeId);
query.executeUpdate();
}

6. 데이터 출력
ResumeController
@GetMapping("/resume/{resumeId}") //
public String detail(@PathVariable int resumeId, HttpServletRequest request, Model model) { // ksj-030810 - model
User sessionUser = (User) session.getAttribute("sessionUser");
ResumeResponse.ResumeDetailDTO resumeDetailDTO = resumeRepository.detail(resumeId); // todo : resumeId가 없는 경우 처리
resumeDetailDTO.isResumeOwner(sessionUser);
request.setAttribute("resumeDetail", resumeDetailDTO);
List<Skill> resumeSkillList = skillRepository.findByResumeId(resumeId);
request.setAttribute("skillList", resumeSkillList);
Double rawRating = ratingRepository.findBySubjectId(resumeDetailDTO.getUserId()); // ksj-030810
if (rawRating != null) {
// 소수점 한자리수까지 출력
String rating = String.format("%.1f", rawRating);
// model.addAttribute("resumeList", resumeDetailDTO.getId()); // ksj-03081
model.addAttribute("rating", rating); // ksj-030810
}
if (sessionUser != null){
Boolean hasRated = ratingRepository.hasRated(sessionUser.getId(), resumeDetailDTO.getUserId());
request.setAttribute("hasRated", hasRated);
Subscribe subscribe = subscribeRepository.findAllByUserIdResumeId(sessionUser.getId(), resumeId);
request.setAttribute("subscribe", subscribe);
}
return "/resume/detail";
}
List<Skill> resumeSkillList = skillRepository.findByResumeId(resumeId);
request.setAttribute("skillList", resumeSkillList);
컨트롤러에서 이력서 ID를 이용해 데이터를 전달한다.
SkillRepositoty
public List<Skill> findByResumeId(int resumeId) {
String q = """
select * from skill_tb where resume_id = ?
""";
Query query = entityManager.createNativeQuery(q,Skill.class);
query.setParameter(1,resumeId);
try {
List<Skill> skillList = query.getResultList();
return skillList;
} catch (Exception e) {
return null;
}
}
resume/detail.mustache
<h4 class="card-title">보유 스킬</h4>
<div class="form-control mb-3">
{{#skillList}}
<btn class="btn btn-light">{{skillName}}</btn>
{{/skillList}}
{{^skillList}}
<btn class="btn btn-outline-light form-control">없음</btn>
{{/skillList}}
</div>

Share article