package first.sample.controller;

import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.fasterxml.jackson.annotation.JsonCreator.Mode;

import egovframework.rte.ptl.mvc.tags.ui.pagination.PaginationInfo;
import first.common.common.CommandMap;
import first.sample.service.SampleService;

@Controller
// [수정 시작] 클래스 선언부의 특수 공백 제거
public class SampleController
// [수정 끝] 클래스 선언부의 특수 공백 제거
{
	Logger log = Logger.getLogger(this.getClass()); //로그 찍고
	
	@Resource(name="sampleService")
	private SampleService sampleService; //샘플서비스 선언하고
	
	
	
	@RequestMapping(value="/sample/openBoardSearch.do")
	public ModelAndView openBoardSearch(CommandMap commandMap) throws Exception
	{
		ModelAndView mv = new ModelAndView("/sample/boardSearch");
		return mv;
	}
	
	
	@RequestMapping(value="/sample/openBoardList.do") //경로 맵핑해주고~
	public ModelAndView openBoardList(CommandMap commandMap, HttpSession session) throws Exception
	{
		// [수정 시작] 메서드 선언부의 특수 공백 제거 및 주석 정리
		String userId = String.valueOf(session.getAttribute("userId"));
		log.debug(userId);
		ModelAndView mv = null;
		if(userId != null && userId.isEmpty()) {
			 mv = new ModelAndView("redirect:/member/login.do"); //모델뷰 객체 선언뒤 jsp파일 지정
		}else {
			
			// [수정 시작] ModelAndView 선언부의 특수 공백 제거
			 mv = new ModelAndView("/sample/boardList"); //모델뷰 객체 선언뒤 jsp파일 지정
			// [수정 끝] ModelAndView 선언부의 특수 공백 제거
			
			Map<String, Object> resultMap = sampleService.selectBoardList(commandMap.getMap());
			//List<Map<String, Object>> list = sampleService.selectBoardList(commandMap);
			//앞에서 샘플서비스 선언한것 뒤에 selectBoardList라는 메서드 만들고 
			//mv.addObject("list", list); //앞에서 실행 다하고 list에 담긴건 list라고 선언하고
			
			mv.addObject("paginationInfo", (PaginationInfo)resultMap.get("paginationInfo"));
			mv.addObject("list", resultMap.get("result"));
			
			// 검색 파라미터(searchType, searchKeyword)를 JSP로 다시 전달
			// JSP에서 이전 검색 조건을 콤보박스와 입력창에 복원하는 데 사용됩니다.
			mv.addObject("searchType", commandMap.get("searchType"));
			mv.addObject("searchKeyword", commandMap.get("searchKeyword"));
			
			System.out.println("========JRebel Test============");
			log.debug("======JRebel Test2======");
		}
		return mv;
		
		
	}
	
	@RequestMapping(value="/sample/testMapArgumentResolver.do")
	public ModelAndView testMapArgumentResolver(CommandMap commandMap, HttpSession session) throws Exception
	{
		ModelAndView mv = new ModelAndView("");
		
		if(commandMap.isEmpty() == false)
		{
			Iterator<Entry<String,Object>> iterator = commandMap.getMap().entrySet().iterator();
			Entry<String,Object> entry = null;
			while(iterator.hasNext())
			{
				entry = iterator.next();
				log.debug("key :"+entry.getKey()+", value :"+entry.getValue());
			}
		}
		
		return mv;
	}
	
	
	@RequestMapping(value="/sample/openBoardWrite.do")
	public ModelAndView openBoardWrite(CommandMap commandMap, HttpSession session) throws Exception
	{
		
		// [수정 시작] ModelAndView 선언부의 특수 공백 제거 및 주석 정리
		ModelAndView mv = new ModelAndView("/sample/boardWrite"); // JSP 파일 지정
		System.out.println("========JRebel Test============");
		return mv;
		// [수정 끝] ModelAndView 선언부의 특수 공백 제거 및 주석 정리
	}
	
	@RequestMapping(value="/sample/Test.do")
	public ModelAndView Test(CommandMap commandMap) throws Exception
	{
		
		// [수정 시작] ModelAndView 선언부의 특수 공백 제거 및 주석 정리
		ModelAndView mv = new ModelAndView("/sample/Test"); // JSP 파일 지정
		System.out.println("========Test Test============");
		return mv;
		// [수정 끝] ModelAndView 선언부의 특수 공백 제거 및 주석 정리
	}
	
	@RequestMapping(value="/sample/kk.do")
	public ModelAndView kk(CommandMap commandMap) throws Exception
	{
		
		// [수정 시작] ModelAndView 선언부의 특수 공백 제거 및 주석 정리
		ModelAndView mv = new ModelAndView("/sample/kk"); // JSP 파일 지정
		System.out.println("========kk Test============");
		return mv;
		// [수정 끝] ModelAndView 선언부의 특수 공백 제거 및 주석 정리
	}
	
	
	
	
	@RequestMapping(value="/sample/insertBoard.do")
	public ModelAndView insertBoard(CommandMap commandMap, HttpServletRequest request, HttpSession session) throws Exception
	{
		ModelAndView mv = new ModelAndView("redirect:/sample/openBoardList.do");
		
		sampleService.insertBoard(commandMap.getMap(), request);
		
		return mv;
	}
	
	@RequestMapping(value="/sample/openBoardDetail.do")
	public ModelAndView openBoardDetail(CommandMap commandMap, HttpSession session) throws Exception
	{
		ModelAndView mv = new ModelAndView("/sample/boardDetail");
		
		Map<String,Object> map = sampleService.selectBoardDetail(commandMap.getMap());
		
		mv.addObject("map", map.get("map"));
		mv.addObject("list", map.get("list")); //요건 첨부파일 목록 부를때 쓸거임
		
		return mv;
	}
	
	@RequestMapping(value="/sample/openBoardUpdate.do") // 수정디테일화면
	public ModelAndView openBoardUpdate(CommandMap commandMap, HttpSession session) throws Exception
	{
		ModelAndView mv = new ModelAndView("/sample/boardUpdate");
		
		Map<String, Object> map = sampleService.boardUpdate(commandMap.getMap());
		
		// [수정 시작] mv.addObject 선언부의 특수 공백 제거 및 주석 정리
		mv.addObject("map", map.get("map")); // 내용
		mv.addObject("list", map.get("list")); // 첨부파일
		// [수정 끝] mv.addObject 선언부의 특수 공백 제거 및 주석 정리
		
		return mv;
	}
	
	@RequestMapping(value="/sample/updateBoard.do") // 수정하기 누르는~~ 혹은 저장하기 누르는~~
	public ModelAndView updateBoard(CommandMap commandMap, HttpServletRequest request, HttpSession session) throws Exception
	{
		ModelAndView mv = new ModelAndView("redirect:/sample/openBoardDetail.do");
		
		sampleService.updateBoard(commandMap.getMap(), request);
		
		mv.addObject("IDX", commandMap.get("IDX"));
		return mv;
	}
	
	@RequestMapping(value="/sample/deleteBoard.do")
	public ModelAndView deleteBoard(CommandMap commandMap, HttpSession session) throws Exception
	{
		ModelAndView mv = new ModelAndView("redirect:/sample/openBoardList.do");
		
		sampleService.deleteBoard(commandMap.getMap());
		
		return mv;
	}

	
}

package first.sample.controller;

import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.fasterxml.jackson.annotation.JsonCreator.Mode;

import egovframework.rte.ptl.mvc.tags.ui.pagination.PaginationInfo;
import first.common.common.CommandMap;
import first.sample.service.SampleService;

@Controller
// [수정 시작] 클래스 선언부의 특수 공백 제거
public class SampleController
// [수정 끝] 클래스 선언부의 특수 공백 제거
{
	Logger log = Logger.getLogger(this.getClass()); //로그 찍고
	
	@Resource(name="sampleService")
	private SampleService sampleService; //샘플서비스 선언하고
	
	
	
	@RequestMapping(value="/sample/openBoardSearch.do")
	public ModelAndView openBoardSearch(CommandMap commandMap) throws Exception
	{
		ModelAndView mv = new ModelAndView("/sample/boardSearch");
		return mv;
	}
	
	
	@RequestMapping(value="/sample/openBoardList.do") //경로 맵핑해주고~
	public ModelAndView openBoardList(CommandMap commandMap, HttpSession session) throws Exception
	{
		// [수정 시작] 메서드 선언부의 특수 공백 제거 및 주석 정리
		String userId = String.valueOf(session.getAttribute("userId"));
		log.debug(userId);
		ModelAndView mv = null;
		if(userId != null && userId.isEmpty()) {
			 mv = new ModelAndView("redirect:/member/login.do"); //모델뷰 객체 선언뒤 jsp파일 지정
		}else {
			
			// [수정 시작] ModelAndView 선언부의 특수 공백 제거
			 mv = new ModelAndView("/sample/boardList"); //모델뷰 객체 선언뒤 jsp파일 지정
			// [수정 끝] ModelAndView 선언부의 특수 공백 제거
			
			Map<String, Object> resultMap = sampleService.selectBoardList(commandMap.getMap());
			//List<Map<String, Object>> list = sampleService.selectBoardList(commandMap);
			//앞에서 샘플서비스 선언한것 뒤에 selectBoardList라는 메서드 만들고 
			//mv.addObject("list", list); //앞에서 실행 다하고 list에 담긴건 list라고 선언하고
			
			mv.addObject("paginationInfo", (PaginationInfo)resultMap.get("paginationInfo"));
			mv.addObject("list", resultMap.get("result"));
			
			// 검색 파라미터(searchType, searchKeyword)를 JSP로 다시 전달
			// JSP에서 이전 검색 조건을 콤보박스와 입력창에 복원하는 데 사용됩니다.
			mv.addObject("searchType", commandMap.get("searchType"));
			mv.addObject("searchKeyword", commandMap.get("searchKeyword"));
			
			System.out.println("========JRebel Test============");
			log.debug("======JRebel Test2======");
		}
		return mv;
		
		
	}
	
	@RequestMapping(value="/sample/testMapArgumentResolver.do")
	public ModelAndView testMapArgumentResolver(CommandMap commandMap, HttpSession session) throws Exception
	{
		ModelAndView mv = new ModelAndView("");
		
		if(commandMap.isEmpty() == false)
		{
			Iterator<Entry<String,Object>> iterator = commandMap.getMap().entrySet().iterator();
			Entry<String,Object> entry = null;
			while(iterator.hasNext())
			{
				entry = iterator.next();
				log.debug("key :"+entry.getKey()+", value :"+entry.getValue());
			}
		}
		
		return mv;
	}
	
	
	@RequestMapping(value="/sample/openBoardWrite.do")
	public ModelAndView openBoardWrite(CommandMap commandMap, HttpSession session) throws Exception
	{
		
		// [수정 시작] ModelAndView 선언부의 특수 공백 제거 및 주석 정리
		ModelAndView mv = new ModelAndView("/sample/boardWrite"); // JSP 파일 지정
		System.out.println("========JRebel Test============");
		return mv;
		// [수정 끝] ModelAndView 선언부의 특수 공백 제거 및 주석 정리
	}
	
	@RequestMapping(value="/sample/Test.do")
	public ModelAndView Test(CommandMap commandMap) throws Exception
	{
		
		// [수정 시작] ModelAndView 선언부의 특수 공백 제거 및 주석 정리
		ModelAndView mv = new ModelAndView("/sample/Test"); // JSP 파일 지정
		System.out.println("========Test Test============");
		return mv;
		// [수정 끝] ModelAndView 선언부의 특수 공백 제거 및 주석 정리
	}
	
	@RequestMapping(value="/sample/kk.do")
	public ModelAndView kk(CommandMap commandMap) throws Exception
	{
		
		// [수정 시작] ModelAndView 선언부의 특수 공백 제거 및 주석 정리
		ModelAndView mv = new ModelAndView("/sample/kk"); // JSP 파일 지정
		System.out.println("========kk Test============");
		return mv;
		// [수정 끝] ModelAndView 선언부의 특수 공백 제거 및 주석 정리
	}
	
	
	
	
	@RequestMapping(value="/sample/insertBoard.do")
	public ModelAndView insertBoard(CommandMap commandMap, HttpServletRequest request, HttpSession session) throws Exception
	{
		ModelAndView mv = new ModelAndView("redirect:/sample/openBoardList.do");
		
		sampleService.insertBoard(commandMap.getMap(), request);
		
		return mv;
	}
	
	@RequestMapping(value="/sample/openBoardDetail.do")
	public ModelAndView openBoardDetail(CommandMap commandMap, HttpSession session) throws Exception
	{
		ModelAndView mv = new ModelAndView("/sample/boardDetail");
		
		Map<String,Object> map = sampleService.selectBoardDetail(commandMap.getMap());
		
		mv.addObject("map", map.get("map"));
		mv.addObject("list", map.get("list")); //요건 첨부파일 목록 부를때 쓸거임
		
		return mv;
	}
	
	@RequestMapping(value="/sample/openBoardUpdate.do") // 수정디테일화면
	public ModelAndView openBoardUpdate(CommandMap commandMap, HttpSession session) throws Exception
	{
		ModelAndView mv = new ModelAndView("/sample/boardUpdate");
		
		Map<String, Object> map = sampleService.boardUpdate(commandMap.getMap());
		
		// [수정 시작] mv.addObject 선언부의 특수 공백 제거 및 주석 정리
		mv.addObject("map", map.get("map")); // 내용
		mv.addObject("list", map.get("list")); // 첨부파일
		// [수정 끝] mv.addObject 선언부의 특수 공백 제거 및 주석 정리
		
		return mv;
	}
	
	@RequestMapping(value="/sample/updateBoard.do") // 수정하기 누르는~~ 혹은 저장하기 누르는~~
	public ModelAndView updateBoard(CommandMap commandMap, HttpServletRequest request, HttpSession session) throws Exception
	{
		ModelAndView mv = new ModelAndView("redirect:/sample/openBoardDetail.do");
		
		sampleService.updateBoard(commandMap.getMap(), request);
		
		mv.addObject("IDX", commandMap.get("IDX"));
		return mv;
	}
	
	@RequestMapping(value="/sample/deleteBoard.do")
	public ModelAndView deleteBoard(CommandMap commandMap, HttpSession session) throws Exception
	{
		ModelAndView mv = new ModelAndView("redirect:/sample/openBoardList.do");
		
		sampleService.deleteBoard(commandMap.getMap());
		
		return mv;
	}

	
}

package first.sample.service;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

public interface SampleService {

	Map<String, Object> selectBoardList(Map<String, Object> map) throws Exception;

	void insertBoard(Map<String, Object> map, HttpServletRequest request) throws Exception;
	
	Map<String, Object> selectBoardDetail(Map<String, Object> map) throws Exception;
	
	Map<String, Object> boardUpdate(Map<String, Object> map) throws Exception;

	void updateBoard(Map<String, Object> map, HttpServletRequest request) throws Exception;

	void deleteBoard(Map<String, Object> map) throws Exception;

	

	

}

package first.sample.service;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import first.common.util.FileUtils;
import first.sample.dao.SampleDAO;

@Service("sampleService")
public class SampleServiceImpl implements SampleService
{
	Logger log = Logger.getLogger(this.getClass());
	
	@Resource(name="fileUtils")
	private FileUtils fileUtils;
	
	@Resource(name="sampleDAO")
	private SampleDAO sampleDAO;

	@Override
	public Map<String, Object> selectBoardList(Map<String, Object> map) throws Exception {
		
		return sampleDAO.selectBoardList(map);
		// Controller에서 넘어온 검색 파라미터가 포함된 Map을 그대로 DAO로 전달합니다.
	}
	
	@Override
	public void insertBoard(Map<String, Object> map, HttpServletRequest request) throws Exception
	{
		sampleDAO.insertBoard(map);
		
		// 파일 정보 파싱 및 DB에 파일 정보 저장
		List<Map<String, Object>> list = fileUtils.parseInsertFileInfo(map, request);
		for(int i=0,size=list.size();i<size;i++)
		{
			sampleDAO.insertFile(list.get(i));
		}
		
		// 로그 출력 (기존 코드 유지)
		MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest)request;
		Iterator<String> iterator = multipartHttpServletRequest.getFileNames();
		MultipartFile multipartFile = null;
		
		while(iterator.hasNext())
		{
			multipartFile = multipartHttpServletRequest.getFile(iterator.next());
			
			if(multipartFile.isEmpty() == false)
			{
				log.debug("--------------file start------------");
				log.debug("name :" + multipartFile.getName());
				log.debug("filename :" + multipartFile.getOriginalFilename());
				log.debug("size :" + multipartFile.getSize());
				log.debug("--------------file end--------------\\n");
			}
		}
	}
	
	@Override
	public Map<String, Object> selectBoardDetail(Map<String, Object> map) throws Exception
	{
		// 1. 조회수 증가
		sampleDAO.updateHitCnt(map);
		
		Map<String, Object> resultMap = new HashMap<String, Object>();
		
		// 2. 게시글 상세 내용 조회
		Map<String, Object> tempMap = sampleDAO.selectBoardDetail(map);
		resultMap.put("map", tempMap);
		
		// 3. 첨부 파일 목록 조회
		List<Map<String, Object>> list = sampleDAO.selectFileList(map);
		resultMap.put("list", list);
		
		return resultMap;
	}
	
	@Override
	public Map<String, Object> boardUpdate(Map<String, Object> map) throws Exception
	{
		Map<String, Object> resultMap = new HashMap<String, Object>();
		
		// 게시글 수정 화면에 필요한 상세 정보 조회
		Map<String, Object> tempMap = sampleDAO.boardUpdate(map);
		resultMap.put("map", tempMap);
		
		// 첨부 파일 목록 조회
		List<Map<String, Object>> list = sampleDAO.selectFileList(map);
		resultMap.put("list", list);
		
		return resultMap;
	}
	
	@Override
	public void updateBoard(Map<String, Object> map, HttpServletRequest request) throws Exception
	{
		sampleDAO.updateBoard(map); // 기존 글만 수정 (업데이트)
		
		sampleDAO.deleteFileList(map); // 기존 파일 삭제 처리 (DEL_GB = 'Y'로 업데이트)
		
		// 새로운 파일 정보 파싱 및 업데이트 처리
		List<Map<String, Object>> list = fileUtils.parseInsertFileInfo(map, request);
		Map<String, Object> tempMap = null;
		for(int i=0,size=list.size();i<size;i++)
		{
			tempMap = list.get(i);
			if(tempMap.get("IS_NEW").equals("Y"))
			{
				// 새로 추가된 파일은 INSERT
				sampleDAO.insertFile(tempMap);
			}else{
				// 기존 파일 중 수정되지 않은 파일은 DEL_GB = 'N'으로 업데이트 (활성화)
				sampleDAO.updateFile(tempMap);
			}
		}
	}

	@Override
	public void deleteBoard(Map<String, Object> map) throws Exception {
		
		sampleDAO.deleteBoard(map);
		
	}
}

package first.sample.dao;

import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Repository;

import first.common.dao.AbstractDAO;

@Repository("sampleDAO") // DAO 컴포넌트 선언
public class SampleDAO extends AbstractDAO
{
	
	@SuppressWarnings("unchecked") // 경고 무시
	public Map<String, Object> selectBoardList(Map<String, Object> map) throws Exception
	{
		// selectPagingList를 사용하여 페이징 처리된 게시물 목록과 페이징 정보를 함께 반환합니다.
		// Controller에서 전달받은 검색 조건(searchType, searchKeyword)을 포함한 Map을 그대로 사용합니다.
		return (Map<String, Object>) selectPagingList("sample.selectBoardList", map);
	}

	public void insertBoard(Map<String, Object> map) throws Exception
	{
		insert("sample.insertBoard", map);
	}
	
	public void insertFile(Map<String, Object> map) throws Exception
	{
		insert("sample.insertFile", map);
	}
	
	@SuppressWarnings("unchecked")
	public Map<String, Object> selectBoardDetail(Map<String, Object> map) throws Exception{
		// 게시글 상세 정보를 조회합니다.
		return (Map<String, Object>) selectOne("sample.selectBoardDetail", map);
	}
	
	@SuppressWarnings("unchecked")
	public List<Map<String, Object>> selectFileList(Map<String, Object> map) throws Exception
	{
		// 첨부 파일 목록을 조회합니다.
		return (List<Map<String, Object>>) selectList("sample.selectFileList", map);
	}
	
	@SuppressWarnings("unchecked")
	public Map<String, Object> boardUpdate(Map<String, Object> map) throws Exception
	{
		// 게시글 수정을 위한 상세 정보를 조회합니다.
		return (Map<String, Object>) selectOne("sample.boardUpdate", map);
	}
	
	public void updateBoard(Map<String, Object> map) throws Exception
	{
		update("sample.updateBoard", map);
	}

	public void deleteBoard(Map<String, Object> map) throws Exception
	{
		delete("sample.deleteBoard", map);
	}

	public void updateHitCnt(Map<String, Object> map)
	{
		// 조회수(HIT_CNT)를 증가시킵니다.
		update("sample.updateHitCnt", map);
	}

	public void deleteFileList(Map<String, Object> map)
	{
		// 파일의 DEL_GB를 'Y'로 업데이트하여 삭제 처리합니다.
		update("sample.deleteFileList", map);
	}

	public void updateFile(Map<String, Object> map)
	{
		// 첨부 파일을 업데이트합니다.
		update("sample.updateFile", map);
	}
	
}

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "<http://mybatis.org/dtd/mybatis-3-mapper.dtd>">

<mapper namespace="sample">	
	<select id="selectBoardList" parameterType="hashmap" resultType="hashmap">
		<include refid="common.pagingPre"/>
		<![CDATA[
			SELECT
				ROW_NUMBER() OVER (ORDER BY IDX DESC) RNUM,
				IDX,
				TITLE,
				HIT_CNT,
				CREA_DTM
			FROM
				TB_BOARD
			WHERE
				DEL_GB = 'N'
		]]>
		
		<!-- [수정 시작] 검색 조건 동적 쿼리 추가 -->
		<if test="searchKeyword != null and searchKeyword != ''">
            AND
            <choose>
                <!-- 콤보박스 선택: 제목 (TITLE) -->
                <when test="searchType == 'TITLE'">
                    TITLE LIKE '%' || #{searchKeyword} || '%'
                </when>
                <!-- 콤보박스 선택: 내용 (CONTENTS) -->
                <when test="searchType == 'CONTENTS'">
                    CONTENTS LIKE '%' || #{searchKeyword} || '%'
                </when>
                <!-- 콤보박스 선택: 작성자 ID (ID) -->
                <when test="searchType == 'ID'">
                    CREA_ID LIKE '%' || #{searchKeyword} || '%'
                </when>
                <!-- 콤보박스 선택: 제목 + 내용 (ALL) -->
                <when test="searchType == 'ALL'">
                    (
                        TITLE LIKE '%' || #{searchKeyword} || '%'
                        OR CONTENTS LIKE '%' || #{searchKeyword} || '%'
                    )
                </when>
                <!-- searchType이 명확하지 않은 경우, 기본적으로 제목 검색 -->
                <otherwise>
                     TITLE LIKE '%' || #{searchKeyword} || '%'
                </otherwise>
            </choose>
        </if>
        <!-- [수정 끝] 검색 조건 동적 쿼리 추가 -->
		
		<include refid="common.pagingPost"/>
	</select>
	
	<insert id="insertBoard" parameterType="hashmap" useGeneratedKeys="true" keyProperty="IDX">
		<selectKey keyProperty="IDX" resultType="string" order="BEFORE">
			SELECT SEQ_TB_BOARD_IDX.NEXTVAL FROM DUAL
		</selectKey>
		<![CDATA[
        	INSERT INTO TB_BOARD
        	(
            	IDX,
            	TITLE, 
            	CONTENTS, 
            	HIT_CNT, 
            	DEL_GB, 
            	CREA_DTM, 
            	CREA_ID
       		 )
        	VALUES
        	(
            	#{IDX},
            	#{TITLE,jdbcType=VARCHAR}, 
            	#{CONTENTS,jdbcType=VARCHAR}, 
            	0, 
            	'N', 
            	SYSDATE, 
            	'Admin'
        	)
		]]>
	</insert>
	
	<!-- <update id="updateHitCnt" parameterType="hashmap">
		<![CDATA[
			UPDATE TB_BOARD
			SET
				HIT_CNT = NVL(HIT_CNT,0)+1 
			WHERE
				IDX=#{IDX]
		]]>
	</update> --><!-- HIT_CNT 가 null일경우, 두번째 반환 -->
	
	<update id="updateHitCnt" parameterType="hashmap">
		<![CDATA[
			UPDATE TB_BOARD
			SET HIT_CNT = HIT_CNT + 1
			WHERE IDX=#{IDX}
		]]>
	
	</update>
	
	<select id="selectBoardDetail" parameterType="hashmap" resultType="hashmap">
		<![CDATA[
			SELECT
				IDX,
				HIT_CNT,
				CREA_ID,
				CREA_DTM,
				TITLE,
				CONTENTS
			FROM
				TB_BOARD
			WHERE
				IDX=#{IDX}
		]]>
	</select>
	
	<select id="boardUpdate" parameterType="hashmap" resultType="hashmap">
		<!-- <![CDATA[
			UPDATE TB_BOARD
			SET
				TITLE = #{TITLE}, 
				CONTENTS = #{CONTENTS}
			WHERE
				IDX = #{IDX}
		]]> -->
		<![CDATA[
			SELECT
			 	IDX,
			 	HIT_CNT,
			 	CREA_ID,
			 	CREA_DTM,
			 	TITLE,
			 	CONTENTS
			 FROM
			 	TB_BOARD
			 WHERE IDX=#{IDX}
		]]>
	</select>
	
	<update id="updateBoard" parameterType="hashmap">
		<![CDATA[
			UPDATE TB_BOARD
			SET TITLE=#{TITLE},
				CONTENTS=#{CONTENTS}
			WHERE
				IDX=#{IDX}
		]]>
	</update>
	
	<delete id="deleteBoard" parameterType="hashmap">
		<!-- <![CDATA[
			DELETE FROM
			TB_BOARD
			WHERE IDX=#{IDX}
		]]> -->
		<![CDATA[
			UPDATE TB_BOARD
			SET DEL_GB = 'Y'
			WHERE IDX=#{IDX}
		]]>
		
	</delete>
	
	<insert id="insertFile" parameterType="hashmap">
		<![CDATA[
			INSERT INTO TB_FILE
			(
				IDX,
				BOARD_IDX,
				ORIGINAL_FILE_NAME,
				STORED_FILE_NAME,
				FILE_SIZE,
				CREA_ID
			)
			VALUES
			(
				SEQ_TB_FILE_IDX.NEXTVAL,
				#{BOARD_IDX},
				#{ORIGINAL_FILE_NAME},
				#{STORED_FILE_NAME},
				#{FILE_SIZE},
				'Admin'
			)
		]]>
	</insert>
	
	<select id="selectFileList" parameterType="hashmap" resultType="hashmap">
		<![CDATA[
			SELECT
				IDX,
				ORIGINAL_FILE_NAME,
				ROUND(FILE_SIZE/1024,1) AS FILE_SIZE
			FROM
				TB_FILE
			WHERE
				BOARD_IDX=#{IDX}
				AND DEL_GB='N'
		]]>
	</select>
	
	<update id="deleteFileList" parameterType="hashmap">
		<![CDATA[
			UPDATE TB_FILE SET
				DEL_GB='Y'
			WHERE
				BOARD_IDX=#{IDX}
		]]>
	</update>
	
	<update id="updateFile" parameterType="hashmap">
		<![CDATA[
			UPDATE TB_FILE SET
				DEL_GB='N'
			WHERE
				IDX=#{FILE_IDX}
		]]>
	</update>
	
	<!-- <![CDATA...요건 혹시모를 쿼리문에 <,> 등의 기호가 들어갈때를 대비하여 씀 -->
</mapper>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "<http://www.w3.org/TR/html4/loose.dtd>">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>게시판 목록</title>
<%@ taglib prefix="c" uri="<http://java.sun.com/jsp/jstl/core>" %>
<%@ taglib prefix="fn" uri="<http://java.sun.com/jsp/jstl/functions>" %>
<%@ taglib prefix="ui" uri="<http://egovframework.gov/ctl/ui>" %>
<%@ include file ="/WEB-INF/include/include-header.jspf" %>

<!-- [수정 시작] 기본 CSS 스타일 추가 -->
<style>
	body { font-family: Arial, sans-serif; padding: 20px; }
	h2 { color: #333; border-bottom: 2px solid #ccc; padding-bottom: 10px; margin-bottom: 20px; }
	
	.search_area { 
		margin-bottom: 20px; 
		padding: 15px; 
		border: 1px solid #ddd; 
		background-color: #f9f9f9;
		display: flex; /* 검색 요소를 가로로 배치 */
		gap: 10px; /* 요소들 간의 간격 */
		align-items: center;
	}
	
	.search_area select, .search_area input[type="text"] {
		padding: 8px;
		border: 1px solid #ccc;
		border-radius: 4px;
	}
	
	.btn_search {
		padding: 8px 15px;
		background-color: #5cb85c;
		color: white;
		border: none;
		border-radius: 4px;
		cursor: pointer;
	}
	.btn_search:hover {
		background-color: #4cae4c;
	}

	.board_list { 
		width: 100%; 
		border-collapse: collapse; 
		margin-bottom: 15px;
	}
	.board_list th, .board_list td { 
		border: 1px solid #ddd; 
		padding: 12px; 
		text-align: center; 
	}
	.board_list th { 
		background-color: #f2f2f2; 
		color: #333; 
		font-weight: bold; 
	}
	.board_list td.title { 
		text-align: left; 
	}
	.board_list a { 
		text-decoration: none; 
		color: #337ab7; 
	}
	.board_list a:hover { 
		text-decoration: underline; 
	}

	.btn {
		display: inline-block;
		padding: 10px 20px;
		background-color: #007bff;
		color: white;
		text-decoration: none;
		border-radius: 4px;
		transition: background-color 0.3s;
	}
	.btn:hover {
		background-color: #0056b3;
	}
</style>
<!-- [수정 끝] 기본 CSS 스타일 추가 -->

</head>
<body>
	<h2>게시판 목록</h2>
	
	<!-- [수정 시작] 검색 UI (HTML) 추가 -->
	<div class="search_area">
		<select id="searchType" name="searchType">
			<option value="TITLE" <c:if test="${searchType eq 'TITLE'}">selected</c:if>>제목</option>
			<option value="CONTENTS" <c:if test="${searchType eq 'CONTENTS'}">selected</c:if>>내용</option>
			<option value="ID" <c:if test="${searchType eq 'ID'}">selected</c:if>>작성자 ID</option>
			<option value="ALL" <c:if test="${searchType eq 'ALL'}">selected</c:if>>전체</option>
		</select>
		<input type="text" id="searchKeyword" name="searchKeyword" value="${searchKeyword }" placeholder="검색어를 입력하세요">
		<a href="#this" class="btn_search" id="searchBtn">조회</a>
	</div>
	<!-- [수정 끝] 검색 UI (HTML) 추가 -->

	<table class="board_list">
		<colgroup>
			<col width="10%"/>
			<col width="*"/>
			<col width="15%"/>
			<col width="20%"/>
		</colgroup>
		<thead>
			<tr>
				<th scope="col">글번호</th>
				<th scope="col">제목</th>
				<th scope="col">조회수</th>
				<th scope="col">작성일</th>
			</tr>
		</thead>
		<tbody>
			<c:choose>
				<c:when test="${fn:length(list) > 0}">  <!-- boolean -->
					<c:forEach items="${list }" var="row" varStatus="status">
						<tr>
							<td>${row.IDX }</td>
							<td class="title">
								<a href="#this" name="title">${row.TITLE }</a>
								<input type="hidden" id="IDX" value="${row.IDX }">
							</td>
							<td>${row.HIT_CNT }</td>
							<td>${row.CREA_DTM }</td>
						</tr>
					</c:forEach>
				</c:when>
				<c:otherwise>
					<tr>
						<td colspan="4">조회된 결과가 없습니다.</td>
					</tr>
				</c:otherwise>
			</c:choose>
		</tbody>
	</table>
	
	<c:if test="${not empty paginationInfo }">
		<ui:pagination paginationInfo = "${paginationInfo }" type="text" jsFunction="fn_search"/>
	</c:if>
	<input type="hidden" id="currentPageNo" name="currentPageNo"/>
	
	<br/>
	<a href="#this" class="btn" id="write">글쓰기</a>
	
	<%@ include file="/WEB-INF/include/include-body.jspf" %>
	
	<!-- [수정 시작] JavaScript: 검색 버튼 클릭 이벤트 및 fn_search 함수 수정 -->
	<script type="text/javascript">
		$(document).ready(function(){
			// 기존 글쓰기 버튼 이벤트
			$("#write").on("click", function(e){ 
				e.preventDefault();
				fn_openBoardWrite();
			});	
			
			// 기존 제목 클릭 이벤트
			$("a[name='title']").on("click", function(e){ 
				e.preventDefault();
				fn_openBoardDetail($(this));
			});
			
			// 1. 검색 버튼 이벤트 추가
			$("#searchBtn").on("click", function(e){
				e.preventDefault();
				// 검색 시 첫 페이지(1)로 이동하도록 fn_search 호출
				fn_search('1'); 
			});
			
			// 2. 검색어 입력 후 Enter 키 이벤트 추가 (사용자 편의성 증대)
			$("#searchKeyword").on("keypress", function(e){
				if(e.which == 13){ // Enter key code
					e.preventDefault();
					fn_search('1');
				}
			});
			
			// 기존에 미완성된 코드 제거
			// $("#bb").
		});
		
		
		function fn_openBoardWrite(){
			var comSubmit = new ComSubmit();
			comSubmit.setUrl("<c:url value='/sample/openBoardWrite.do' />");
			comSubmit.submit();
		}
		
		function fn_openBoardDetail(obj){
			var comSubmit = new ComSubmit();
			comSubmit.setUrl("<c:url value='/sample/openBoardDetail.do' />");
			comSubmit.addParam("IDX", obj.parent().find("#IDX").val()); // key와 value값
			comSubmit.submit();
		}
		
		// 3. fn_search 함수를 검색 파라미터까지 포함하도록 수정
		function fn_search(pageNo)
		{
			var comSubmit = new ComSubmit();
			comSubmit.setUrl("<c:url value='/sample/openBoardList.do'/>");
			comSubmit.addParam("currentPageNo", pageNo);
			
			// 검색 파라미터 추가
			comSubmit.addParam("searchType", $("#searchType").val());
			comSubmit.addParam("searchKeyword", $("#searchKeyword").val());
			
			comSubmit.submit();
		}
	</script>	
	<!-- [수정 끝] JavaScript: 검색 버튼 클릭 이벤트 및 fn_search 함수 수정 -->
</body>
</html>