115일차

1. 채용공고 목록

1.1 채용공고 목록 view

단순히 검색 뿐만 아니라 업종별, 경력별, 학력별, 계약별로 검색을 하기 위해서 체크박스를 각각 만들고 체크박스의 값에 따라 검색이 되도록 했다.

<form action="#">
    <div class="container-lg mt-3">
        <div class="accordion" id="accordionExample">
            <div class="accordion-item">
                <h2 class="accordion-header">
                    <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
                            data-bs-target="#collapseOne"
                            aria-expanded="true"
                            aria-controls="collapseOne">
                        필터링하기
                    </button>
                </h2>

                <div id="collapseOne" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
                    <div class="accordion-body">
                        <div class="row">
                            <div class="col border-secondary border-end">
                                <h5>업종</h5>
                                <div id="industry-container"></div>
                            </div>
                            <div class="col border-secondary border-end">
                                <h5>경력</h5>
                                <div class="form-check">
                                    <input ${param.experienceLevels eq '신입'? 'checked' : ''}
                                            class="form-check-input"
                                            name="experienceLevels"
                                            type="checkbox"
                                            value="신입">
                                    <label class="form-check-label">
                                        신입
                                    </label>
                                </div>
                                <div class="form-check">
                                    <input ${param.experienceLevels eq '경력'? 'checked' : ''}
                                            class="form-check-input"
                                            name="experienceLevels"
                                            type="checkbox"
                                            value="경력">
                                    <label class="form-check-label">
                                        경력
                                    </label>
                                </div>
                                <div class="form-check">
                                    <input ${param.experienceLevels eq '경력무관'? 'checked' : ''}
                                            class="form-check-input"
                                            name="experienceLevels"
                                            type="checkbox"
                                            value="경력무관">
                                    <label class="form-check-label">
                                        경력무관
                                    </label>
                                </div>
                            </div>
                            <div class="col border-secondary border-end">
                                <h5>학력</h5>
                                <div class="form-check">
                                    <input ${param.educationLevels eq '대학원이상'? 'checked' : ''}
                                            class="form-check-input"
                                            name="educationLevels"
                                            type="checkbox"
                                            value="대학원">
                                    <label class="form-check-label">
                                        대학원이상
                                    </label>
                                </div>
                                <div class="form-check">
                                    <input ${param.educationLevels eq '대졸이상'? 'checked' : ''}
                                            class="form-check-input"
                                            name="educationLevels"
                                            type="checkbox"
                                            value="학사">
                                    <label class="form-check-label">
                                        대졸이상
                                    </label>
                                </div>
                                <div class="form-check">
                                    <input ${param.educationLevels eq '초대졸이상'? 'checked' : ''}
                                            class="form-check-input"
                                            name="educationLevels"
                                            type="checkbox"
                                            value="전문학사">
                                    <label class="form-check-label">
                                        초대졸이상
                                    </label>
                                </div>
                                <div class="form-check">
                                    <input ${param.educationLevels eq '고졸이상'? 'checked' : ''}
                                            class="form-check-input"
                                            name="educationLevels"
                                            type="checkbox"
                                            value="고등학교">
                                    <label class="form-check-label">
                                        고졸이상
                                    </label>
                                </div>
                                <div class="form-check">
                                    <input ${param.educationLevels eq '학력무관'? 'checked' : ''}
                                            class="form-check-input"
                                            name="educationLevels"
                                            type="checkbox"
                                            value="학력무관">
                                    <label class="form-check-label">
                                        학력무관
                                    </label>
                                </div>
                            </div>
                            <div class="col">
                                <h5>채용형태</h5>
                                <div class="form-check">
                                    <input ${param.employmentTypes eq '정규직'? 'checked' : ''}
                                            class="form-check-input"
                                            name="employmentTypes"
                                            type="checkbox"
                                            value="정규직">
                                    <label class="form-check-label">
                                        정규직
                                    </label>
                                </div>
                                <div class="form-check">
                                    <input ${param.employmentTypes eq '계약직'? 'checked' : ''}
                                            class="form-check-input"
                                            name="employmentTypes"
                                            type="checkbox"
                                            value="계약직">
                                    <label class="form-check-label">
                                        계약직
                                    </label>
                                </div>
                                <div class="form-check">
                                    <input ${param.employmentTypes eq '인턴'? 'checked' : ''}
                                            class="form-check-input"
                                            name="employmentTypes"
                                            type="checkbox"
                                            value="인턴">
                                    <label class="form-check-label">
                                        인턴
                                    </label>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="d-flex justify-content-end mt-2">
            <div>
                <div class="input-group">
                    <select name="type" class="form-select flex-grow-0" style="width: 120px;">
                        <option value="all">전체</option>
                        <option ${param.type eq 'companyName'? 'selected' : ''} value="companyName">기업명</option>
                        <option ${param.type eq 'title'? 'selected' : ''} value="title">공고명</option>
                        <option ${param.type eq 'address'? 'selected' : ''} value="address">지역</option>
                    </select>
                    <input name="search" value="${param.search}" class="form-control" type="search" placeholder="Search"
                           aria-label="Search">
                    <button id="search-btn" class="btn btn-outline-success" type="submit">
                        검색
                    </button>
                    <c:if test="${not empty param}">
                        <div class="ms-3">
                            <button class="btn btn-outline-danger" type="button" id="cancel-search-btn">
                                <i class="fa-solid fa-x"></i>
                            </button>
                        </div>
                    </c:if>
                </div>
            </div>
        </div>
    </div>
</form>

이전 검색들은 따로 검색창에 파라미터가 보일필요가 없었으나 이번 검색은 해당 파라미터로 와서 찾아볼 수도 있도록 form태그를 사용해서 파라미터에 get요청이 되도록 했다.

1.2 컨트롤러

@RestController
@RequestMapping("/api/user/posting")
@RequiredArgsConstructor
@Slf4j
public class UserPostingControllerAPI {

    private final UserPostingService userPostingService;

    @GetMapping
    public ResponseEntity<Map<String, Object>> getPostings(
            @RequestParam(value = "industrIds", required = false) Integer[] industrIds,
            @RequestParam(value = "experienceLevels", required = false) String[] experienceLevels,
            @RequestParam(value = "educationLevels", required = false) String[] educationLevels,
            @RequestParam(value = "employmentTypes", required = false) String[] employmentTypes,
            @RequestParam(value = "type", required = false) String type,
            @RequestParam(value = "search", defaultValue = "") String search) {

        Map<String, Object> result = userPostingService.getPostings(industrIds, experienceLevels, educationLevels, employmentTypes, type, search);
        return ResponseEntity.ok(result);
    }
}

체크박스들이기 때문에 같은 이름의 체크박스들이 배열로 값이 들어오게 된다. 이외에는 다를게 없다.

1.3 서비스

서비스에서는 아직 페이징이 없기 때문에 매퍼에 배열을 담아서 보내준다.

@Override
    public Map<String, Object> getPostings(Integer[] industrIds, String[] experienceLevels, String[] educationLevels, String[] employmentTypes, String type, String search) {

        List<Posting> postingList = postingMapper.getNowPostingsAll(industrIds, experienceLevels, educationLevels, employmentTypes, type, search);

        List<Industry> industryList = industryService.getIndustryList();

        return Map.of("postingList", postingList, "industryList", industryList);
    }

1.4 매퍼

검색은 평소와 같고 배열들을 받아서 넣는 동적쿼리를 사용했다.
foreach로 배열(컬렉션을 받는데) item으로 하나씩 꺼내서 넣는다.
open에는 시작 값 separtor는 반복해서 넣을때의 구분자 close는 닫은 것이다.

시작값은 컬럼명 IN (
구분자는 ,
끝은 )로해서

동적으로 쿼리가 생성될때 반복해서 ()안에 값들이 , 로 구분되어 들어가게 된다.
만약 industryIds가 있다면 industry_id IN (1, 2, 3, 4, 5)이런식으로 완성되어 가게된다.

  <select id="getNowPostingsAll" resultMap="postingViewMap">
      <bind name="pattern" value="'%' + search + '%'"/>
      SELECT * FROM (SELECT * FROM VIEW_POSTING WHERE status = '채용중') c
      <where>
          <if test="type eq 'all'">
              (company_name LIKE #{pattern} OR title LIKE #{pattern} OR address LIKE #{pattern})
          </if>
          <if test="type eq 'companyName'">
              OR company_name LIKE #{pattern}
          </if>
          <if test="type eq 'title'">
              OR title LIKE #{pattern}
          </if>
          <if test="type eq 'address'">
              OR address LIKE #{pattern}
          </if>
          <if test="industrIds neq null">
              AND
              <foreach item="item" index="index" collection="industrIds"
                       open="industry_id IN (" separator="," close=")" nullable="true">
                  #{item}
              </foreach>
          </if>
          <if test="experienceLevels neq null">
              AND
              <foreach item="item" index="index" collection="experienceLevels"
                       open="experience_level IN (" separator="," close=")" nullable="true">
                  #{item}
              </foreach>
          </if>
          <if test="educationLevels neq null">
              AND
              <foreach item="item" index="index" collection="educationLevels"
                       open="education_level IN (" separator="," close=")" nullable="true">
                  #{item}
              </foreach>
          </if>
          <if test="employmentTypes neq null">
              AND
              <foreach item="item" index="index" collection="employmentTypes"
                       open="employment_type IN (" separator="," close=")" nullable="true">
                  #{item}
              </foreach>
          </if>
      </where>
      LIMIT 0, 10
  </select>

2023.07.29

if, foreach를 통해서 동적sql을 잘 작성하는 것이 목표이다.
다른 db소통방식을 사용하게 된다면 다른 문법을 배워야하겟지만
그래도 기본은 비슷하다고 생각된다.

+ Recent posts