더북(TheBook)

DotNetNoteController.cs 파일에 다음과 같이 코드를 작성한다. 각각의 페이지를 제작할 때마다 컨트롤러에 액션 메서드를 추가하는 방식으로 하나씩 코드를 늘려나가도 되는데, 여기서는 편의상 DotNetNote 컨트롤러의 모든 코드를 미리 제시하겠다.

▼  /Controllers/DotNetNoteController.cs

using DotNetNote.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace DotNetNote.Controllers
{
  public class DotNetNoteController : Controller
  {
      // [DNN] 의존성 주입
      private IHostingEnvironment _environment; // 환경 변수
      private INoteRepository _repository; // 게시판 리파지터리
      private INoteCommentRepository _commentRepository; // 댓글 리파지터리
      
      public DotNetNoteController(
          IHostingEnvironment environment,
          INoteRepository repository,
          INoteCommentRepository commentRepository)
      {
          _environment = environment;
          _repository = repository;
          _commentRepository = commentRepository;
      }
      
      // 공통 속성: 검색 모드: 검색 모드이면 true, 그렇지 않으면 false
      public bool SearchMode { get; set; } = false;
      public string SearchField { get; set; } // 필드: Name, Title, Content
      public string SearchQuery { get; set; } // 검색 내용
      
      /// <summary>
      /// 현재 보여줄 페이지 번호
      /// </summary>
      public int PageIndex { get; set; } = 0;
      
      /// <summary>
      /// 총 레코드 갯수(글번호 순서 정렬용)
      /// </summary>
      public int TotalRecordCount { get; set; } = 0;
      
      /// <summary>
      /// 게시판 리스트 페이지
      /// </summary>
      public IActionResult Index()
      {
          // 검색 모드 결정: ?SearchField=Name&SearchQuery=닷넷코리아 
          SearchMode = (
              !string.IsNullOrEmpty(Request.Query[“SearchField”]) &&
              !string.IsNullOrEmpty(Request.Query[“SearchQuery”])
          );
          
          // 검색 환경이면 속성에 저장
          if (SearchMode)
          {
              SearchField = Request.Query[“SearchField”].ToString();
              SearchQuery = Request.Query[“SearchQuery”].ToString();
          }
          
          // [1] 쿼리스트링에 따른 페이지 보여주기
          if (!string.IsNullOrEmpty(Request.Query[“Page”].ToString()))
          {
              // Page는 보여지는 쪽은 1, 2, 3, … 코드단에서는 0, 1, 2, …
              PageIndex = Convert.ToInt32(Request.Query[“Page”]) - 1;
          }
          
          // [2] 쿠키를 사용한 리스트 페이지 번호 유지 적용(Optional): 
          //    100번째 페이지 보고 있다가 다시 리스트 왔을 때 100번째 페이지 표시
          if (!string.IsNullOrEmpty(Request.Cookies[“DotNetNotePageNum”]))
          {
              if (!String.IsNullOrEmpty(
                  Request.Cookies[“DotNetNotePageNum”]))
              {
                  PageIndex =
                      Convert.ToInt32(Request.Cookies[“DotNetNotePageNum”]);
              }
              else
              {
                  PageIndex = 0;
              }
          }
          
          // 게시판 리스트 정보 가져오기
          IEnumerable<Note> notes;
          if (!SearchMode)
          {
              TotalRecordCount = _repository.GetCountAll();
              notes = _repository.GetAll(PageIndex);
          }
          else
          {
              TotalRecordCount = _repository.GetCountBySearch(
                  SearchField, SearchQuery);
              notes = _repository.GetSeachAll(
                  PageIndex, SearchField, SearchQuery);
          }
          
          // 주요 정보를 뷰 페이지로 전송
          ViewBag.TotalRecord = TotalRecordCount;
          ViewBag.SearchMode  = SearchMode;
          ViewBag.SearchField = SearchField;
          ViewBag.SearchQuery = SearchQuery;
          
          return View(notes);
      }
      
      /// <summary>
      /// 게시판 글쓰기 폼
      /// </summary>
        [HttpGet]
      public IActionResult Create()
      {
          // 글쓰기 폼은 입력, 수정, 답변에서 _BoardEditorForm.cshtml 공유함
          ViewBag.FormType = BoardWriteFormType.Write;
          ViewBag.TitleDescription = “글 쓰기 - 다음 필드들을 채워주세요.”;
          ViewBag.SaveButtonText = “저장”;
          
          return View();
      }
      
      /// <summary>
      /// 게시판 글쓰기 처리 + 파일 업로드 처리
      /// </summary>
        [HttpPost]
      public async Task<IActionResult> Create(
          Note model, ICollection<IFormFile> files)
      {
          // 파일 업로드 처리 시작
          string fileName = String.Empty;
          int fileSize = 0;
          
          var uploadFolder = Path.Combine(_environment.WebRootPath, “files”);
          
          foreach (var file in files)
          {
              if (file.Length > 0)
              {
                  fileSize = Convert.ToInt32(file.Length);
                  // 파일명 중복 처리
                  fileName = Dul.FileUtility.GetFileNameWithNumbering(
                      uploadFolder, Path.GetFileName(
                          ContentDispositionHeaderValue.Parse(
                              file.ContentDisposition).FileName.Trim(’“’)));
                  // 파일 업로드
                  using (var fileStream = new FileStream(
                      Path.Combine(uploadFolder, fileName), FileMode.Create))
                  {
                      await file.CopyToAsync(fileStream);
                  }
              }
          }
          
          Note note = new Note();
          
          note.Name     = model.Name;
          note.Email    = Dul.HtmlUtility.Encode(model.Email);
          note.Homepage = model.Homepage;
          note.Title    = Dul.HtmlUtility.Encode(model.Title);
          note.Content  = model.Content;
          note.FileName = fileName;
          note.FileSize = fileSize;
          note.Password = model.Password;
          note.PostIp =
              HttpContext.Connection.RemoteIpAddress.ToString(); // IP 주소
          note.Encoding = model.Encoding;
          
          _repository.Add(note); // 데이터 저장
          
          // 데이터 저장 후 리스트 페이지 이동시 toastr로 메시지 출력
          TempData[“Message”] = “데이터가 저장되었습니다.”;
          
          return RedirectToAction(“Index”); // 저장 후 리스트 페이지로 이동
      }
      
      
      /// <summary>
      /// 게시판 파일 강제 다운로드 기능(/BoardDown/:Id)
      /// </summary>
      public FileResult BoardDown(int id)
      {
          string fileName = ””;
          
          // 넘어온 번호에 해당하는 파일명 가져오기(보안때문에… 파일명 숨김)
          fileName = _repository.GetFileNameById(id);
          
          if (fileName == null)
          {
              return null;
          }
          else
          {
              // 다운로드 카운트 증가 메서드 호출
              _repository.UpdateDownCountById(id);
              
              byte[] fileBytes = System.IO.File.ReadAllBytes(Path.Combine(
                  _environment.WebRootPath, “files”) + “\“ + fileName);
              
              return File(fileBytes, “application/octet-stream”, fileName);
          }
      }
      
      /// <summary>
      /// 게시판의 상세보기 페이지(Details, BoardView)
      /// </summary>
      public IActionResult Details(int id)
      {
          // 넘어온 Id 값에 해당하는 레코드 하나 읽어서 Note 클래스에 바인딩
          var note = _repository.GetNoteById(id);
          
          // [!] 인코딩 방식에 따른 데이터 출력
          ContentEncodingType encoding = (ContentEncodingType)Enum.Parse(
              typeof(ContentEncodingType), note.Encoding);
          string encodedContent = ””;
          switch (encoding)
          {
              // Text: 소스 그대로 표현
              case ContentEncodingType.Text:
                  encodedContent =
                      Dul.HtmlUtility.EncodeWithTabAndSpace(note.Content);
                  break;
              // Html: HTML 형식으로 출력
              case ContentEncodingType.Html:
                  encodedContent = note.Content; // 변환 없음
                  break;
              // Mixed: 엔터 처리만
              case ContentEncodingType.Mixed:
                  encodedContent = note.Content.Replace(“\r\n”, “<br />“);
                  break;
              // Html: 기본
              default:
                  encodedContent = note.Content; // 변환 없음
                  break;
          }
          ViewBag.Content = encodedContent; //[!]
          
          // 첨부된 파일 확인
          if (note.FileName.Length > 1)
          {
              // [a] 파일 다운로드 링크: String.Format()으로 표현해 봄 
              ViewBag.FileName = String.Format(
                  “<a href=‘/DotNetNote/BoardDown?Id={0}’>“
                  + ”{1}{2} / 전송수: {3}</a>“, note.Id
                  , “<img src=\“/images/ext/ext_zip.gif\” border=\“0\”>“
                  , note.FileName, note.DownCount);
              // [b] 이미지 미리보기: C# 6.0 String 보간법으로 표현해 봄
              if (Dul.BoardLibrary.IsPhoto(note.FileName))
              {
                  ViewBag.ImageDown =
                      $“<img src=\‘/DotNetNote/ImageDown/{note.Id}\’><br />“;
              }
          }
          else
          {
              ViewBag.FileName = ”(업로드된 파일이 없습니다.)”;
          }
          
          // 현재 글에 해당하는 댓글 리스트와 현재 글 번호를 담아서 전달
          NoteCommentViewModel vm = new NoteCommentViewModel();
          vm.NoteCommentList = _commentRepository.GetNoteComments(note.Id);
          vm.BoardId = note.Id;
          ViewBag.CommentListAndId = vm;
          
          return View(note);
      }
      
      /// <summary>
      /// 게시판 삭제 폼
      /// </summary>
        [HttpGet]
      public IActionResult Delete(int id)
      {
          ViewBag.Id = id;
          return View();
      }
      
      /// <summary>
      /// 게시판 삭제 처리
      /// </summary>
        [HttpPost]
      public IActionResult Delete(int id, string Password)
      {
          if (_repository.DeleteNote(id, Password) > 0)
          {
              TempData[“Message”] = “데이터가 삭제되었습니다.”;
              
              // 학습 목적으로 삭제 후의 이동 페이지를 2군데 중 하나로 분기
              if (DateTime.Now.Second % 2 == 0)
              {
                  // [a] 삭제 후 특정 뷰 페이지로 이동
                  return RedirectToAction(“DeleteCompleted”);
              }
              else
              {
                  // [b] 삭제 후 Index 페이지로 이동
                  return RedirectToAction(“Index”);
              }
          }
          else
          {
              ViewBag.Message = “삭제되지 않았습니다. 비밀번호를 확인하세요.”;
          }
          
          ViewBag.Id = id;
          return View();
      }
      
      /// <summary>
      /// 게시판 삭제 완료 후 추가적인 처리할 때 페이지
      /// </summary>
      public IActionResult DeleteCompleted()
      {
          return View();
      }
      
      /// <summary>
      /// 게시판 수정 폼
      /// </summary>
        [HttpGet]
      public IActionResult Edit(int id)
      {
          ViewBag.FormType = BoardWriteFormType.Modify;
          ViewBag.TitleDescription = “글 수정 - 아래 항목을 수정하세요.”;
          ViewBag.SaveButtonText = “수정”;
          
          // 기존 데이터를 바인딩
          var note = _repository.GetNoteById(id);
          
          // 첨부된 파일명 및 파일 크기 기록
          if (note.FileName.Length > 1)
          {
              ViewBag.FileName = note.FileName;
              ViewBag.FileSize = note.FileSize;
              ViewBag.FileNamePrevious =
                  $“기존에 업로드된 파일명: {note.FileName}”;
          }
          else
          {
              ViewBag.FileName = ””;
              ViewBag.FileSize = 0;
          }
          
          return View(note);
      }
      
      /// <summary>
      /// 게시판 수정 처리 + 파일 업로드
      /// </summary>
        [HttpPost]
      public async Task<IActionResult> Edit(
          Note model, ICollection<IFormFile> files,
          int id, string previousFileName = ””, int previousFileSize = 0)
      {
          ViewBag.FormType = BoardWriteFormType.Modify;
          ViewBag.TitleDescription = “글 수정 - 아래 항목을 수정하세요.”;
          ViewBag.SaveButtonText = “수정”;
          
          string fileName = ””;
          int fileSize = 0;
          
          if (previousFileName != null)
          {
              fileName = previousFileName;
              fileSize = previousFileSize;
          }
          
          // 파일 업로드 처리 시작
          var uploadFolder = Path.Combine(_environment.WebRootPath, “files”);
          
          foreach (var file in files)
          {
              if (file.Length > 0)
              {
                  fileSize = Convert.ToInt32(file.Length);
                  // 파일명 중복 처리
                  fileName = Dul.FileUtility.GetFileNameWithNumbering(
                      uploadFolder, Path.GetFileName(
                          ContentDispositionHeaderValue.Parse(
                              file.ContentDisposition).FileName.Trim(’“’)));
                  // 파일 업로드
                  using (var fileStream = new FileStream(
                      Path.Combine(uploadFolder, fileName), FileMode.Create))
                  {
                      await file.CopyToAsync(fileStream);
                  }
              }
          }
          
          Note note = new Note();
          
          note.Id = id;
          note.Name     = model.Name;
          note.Email    = Dul.HtmlUtility.Encode(model.Email);
          note.Homepage = model.Homepage;
          note.Title    = Dul.HtmlUtility.Encode(model.Title);
          note.Content  = model.Content;
          note.FileName = fileName;
          note.FileSize = fileSize;
          note.Password = model.Password;
          note.ModifyIp =
              HttpContext.Connection.RemoteIpAddress.ToString(); // IP 주소
          note.Encoding = model.Encoding;
          
          int r = _repository.UpdateNote(note); // 데이터베이스에 수정 적용
          if (r > 0)
          {
              TempData[“Message”] = “수정되었습니다.”;
              return RedirectToAction(“Details”, new { Id = id });
          }
          else
          {
              ViewBag.ErrorMessage =
                  “업데이트가 되지 않았습니다. 암호를 확인하세요.”;
              return View(note);
          }
      }
      
      /// <summary>
      /// 답변 글쓰기 폼
      /// </summary>
      /// <param name=“id”>부모글 Id</param>
        [HttpGet]
      public IActionResult Reply(int id)
      {
          ViewBag.FormType = BoardWriteFormType.Reply;
          ViewBag.TitleDescription = “글 답변 - 다음 필드들을 채워주세요.”;
          ViewBag.SaveButtonText = “답변”;
          
          // 기존 데이터를 바인딩
          var note = _repository.GetNoteById(id); // 기존 부모글 Id
          
          // 새로운 Note 개체 생성
          var newNote = new Note();
          
          // 기존 글의 제목과 내용을 새 Note 개체에 저장 후 전달
          newNote.Title = $“Re : {note.Title}”;
          newNote.Content =
              $“\n\nOn {note.PostDate}, ‘{note.Name}’ wrote:\n———-\n>“
              + $”{note.Content.Replace(”<span class=“n”>n”, “<span class=“n”>n

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.