사이드바에 사용자 지정 링크 섹션을 추가하는 기능

사용자가 사이드바에 커스텀 링크 섹션을 추가할 수 있도록 허용하고자 합니다.

이 기능의 첫 번째 단계로, 기존 사이드바 설정 페이지를 기반으로 하여 사용자가 이러한 링크를 위한 단일 커스텀 섹션을 추가할 수 있도록 할 계획입니다.

다음과 같이 작동할 수 있다고 생각합니다:

  1. “커스텀 섹션 표시” 체크박스.
    체크하면 커스텀 섹션이 카테고리 섹션 위에 나타나며, "변경 사항 저장"을 하려면 유효한 이름과 최소 한 개의 유효한 링크가 필요합니다.
  2. 커스텀 섹션 이름 입력 텍스트 상자 (기본값: My Links, 빈 값 불가)
  3. “링크 추가” 버튼. 클릭 시 링크 추가를 위한 대화상자가 표시됩니다.
  4. 추가된 링크에는 해당 링크를 제거하는 버튼이 표시됩니다.

링크 추가 대화상자

  1. 링크 추가 대화상자에는 이름, URL 두 개의 텍스트 상자가 있습니다.
  2. 이름은 빈 값이 될 수 없으며, 이모지 포함 가능 (사이드바에 렌더링되어야 함)
  3. URL은 동일한 Discourse 사이트 내 링크여야 합니다*
  4. 저장 시 링크가 추가되고, 취소 시 변경 사항이 폐기됩니다.

유효한 커스텀 섹션이 설정되고 저장되면, 사이드바에 해당 섹션이 “카테고리” 위에 표시됩니다.

* 왜?

  1. 해당 페이지에 있을 때 사이드바의 링크를 “강조”/"볼드"로 표시하고 싶습니다.
  2. 사이트 이름 변경 시에도 의도대로 작동하도록 “path” 부분을 저장하고 싶습니다.

오늘날 링크를 추가하는 것이 가치 있다고 생각하는 이유는, 이를 통해 "열린 사이드바 버그"와 같은 항목에 쉽게 접근할 수 있게 하고, 개선된 토픽 목록 필터와 같은 토픽 목록의 향후 개선 사항을 활용할 수 있게 되기 때문입니다. URL만 있다면 누구나 사이드바에 추가할 수 있습니다.

사용자가 여러 섹션을 추가하고 순서를 변경하기를 원할 것이라고 생각하지만, 초기에는 이를 범위에서 제외할 것입니다.

16개의 좋아요

이 부분에 대해 한 가지 질문이 있습니다, @mcwumbly. 이 기능은 사용자가 사이드바에 개인 링크를 추가할 수 있도록 하는 전략처럼 보입니다. 그렇다면 이 방식으로 사이트 소유자가 모든 사용자에게 표시할 링크를 추가할 수도 있을까요?

스포일러 - 제 생각에는 가능해야 합니다. :slight_smile:

5개의 좋아요

네, 그렇게 확장할 수 있다고 생각합니다. 그리고 일부 사이트에서는 사용자용 기능보다 훨씬 더 유용한 기능이 될 수 있다고 봅니다.

8개의 좋아요

정말 좋은 아이디어입니다. :+1:

몇 가지 질문이 있습니다:

  1. 사용자가 추가할 수 있는 커스텀 링크에 수량 제한이 있나요?

  2. 사이드바의 링크 끝부분이 아래 목업처럼 잘릴까요? (그렇게 되어도 괜찮습니다.)
    그렇게 된다면, 마우스 커서를 링크 위에 올리면 툴팁을 통해 전체 텍스트를 볼 수 있을까요?

좋은 질문입니다. 제한을 두어야 할 가능성이 높지만, 아직 자세히 논의된 사항은 아닙니다. 어떤 종류의 제한을 원하시나요 (또는 피하고 싶으신가요)?

1개의 좋아요

처음에는 10개의 링크면 충분할 것 같았지만, 다른 포럼 회원들은 더 많은 링크를 원할 것 같습니다. 아마도 20개 정도가 적당할 것 같네요. 그래서 저는 20개로 시작해서 사용자들의 피드백을 기다려보고, 더 많은 링크를 요청하는지 확인해볼 생각입니다(하지만 20개도 이미 좋은 숫자라고 생각합니다).

1개의 좋아요

이 기능의 "사용자 친화성"을 확장하기 위해, 현재 Discourse 페이지의 링크(모든 링크)를 사이드바에 직접 드래그 앤 드롭할 수 있게 하면 꽤 좋을 것 같습니다.

(이전에 “내 링크” DIV에 추가된 링크들 사이의) 포인터가 링크가 배치될 위치를 표시합니다.

모ック업 (완전 크기로 애니메이션을 보려면 우클릭 > “새 탭에서 이미지 열기”):

anim01

아마도 다음 페이지가 드래그 앤 드롭 구현에 도움이 될 수 있습니다.

7개의 좋아요

사이트에 사용자가 닫을 수 있는 기본 링크를 설정할 수 있으면 좋겠습니다. 제 생각에, 현재 사용 중인 커스텀 헤더 링크보다 이 방식이 확실히 더 낫습니다.

3개의 좋아요

커스텀 링크 추가에 동의합니다. 원래 드롭다운 메뉴에 몇 가지 커스텀 링크가 추가되어 있었습니다. Discourse 자체가 “More” 옵션 아래에 "생일"과 "문서"를 표시하는 것과 유사하게, 그 링크들을 다시 추가하면 좋겠습니다.

4개의 좋아요

여러분 안녕하세요!

새 사이드바에 링크를 추가하는 방법을 고민 중입니다.

해당 영역에 커넥터가 없는 것 같습니다…

가장 효과적인 방법은 무엇일까요?


수정

제 질문이 여기로 이동되었습니다… 왜 그런지 모르겠지만, 사실 저는 동일한 것에 대해 묻고 있습니다.

어쨌든, 제가 해결하기 위해 한 작업은 다음과 같습니다. 이 방법은 아름답지 않으며, discourse의 템플릿 시스템과 통합할 수 있는 방법이 있다면 훨씬 나을 것입니다.

다음과 같습니다:

<script>
    let logo = "" // SVG 경로는 여러분이 필요 없으므로 제거했습니다.
    const div = document.createElement('div') // 추가할 링크 생성
    div.className = 'sidebar-section-link-wrapper' // 관련 클래스 추가
    div.innerHTML = `
          <a title="Tous les sujets" href="https://www.latranchee.com" id="ember12" class="sidebar-section-link sidebar-section-link-everything sidebar-row ember-view">
            <span class="sidebar-section-link-prefix icon">
              <svg viewBox="0 0 100 100" class="logoIcon" xmlns="http://www.w3.org/2000/svg">${logo}</svg>
            </span>
            <span class="sidebar-section-link-content-text"> Accueil </span>
          </a>
    ` // 링크 내용 채우기
    
    $( document ).ready(() => { // 이는 ember가 작업을 마칠 때까지 기다리기 위해 필요합니다
        // 데스크톱에 내비게이션 추가
        let desktop = document.getElementsByClassName('sidebar-section-content')[0];
        if(desktop) desktop.prepend(div)
        
        // 모바일에 내비게이션 추가
        let hamburger = document.getElementById('toggle-hamburger-menu').addEventListener("click", addMobileNav);
        function addMobileNav () {
            setTimeout(function(){ // 내비게이션이 로드될 때까지 기다리게 강제
                document.getElementsByClassName('sidebar-section-content')[0].prepend(div);
            }, 0);
        }
    })
</script>

데스크톱에서의 결과…

& 모바일에서의 결과:

더 나은 방법이 있을 때까지는 이걸로 해야 할 것 같습니다!

수정 #2

코드를 정리하여 내비게이션이 객체 배열에서 로드되도록 했습니다.

<script>
  let rss = `<path d="M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3M7.5,15A1.5,1.5 0 0,0 6,16.5A1.5,1.5 0 0,0 7.5,18A1.5,1.5 0 0,0 9,16.5A1.5,1.5 0 0,0 7.5,15M6,10V12A6,6 0 0,1 12,18H14A8,8 0 0,0 6,10M6,6V8A10,10 0 0,1 16,18H18A12,12 0 0,0 6,6Z"></path>`
  let mdiSchool = `<path d="M12,3L1,9L12,15L21,10.09V17H23V9M5,13.18V17.18L12,21L19,17.18V13.18L12,17L5,13.18Z"></path>`
  let logo = `<g xmlns="http://www.w3.org/2000/svg" fill="#0e1e2b">
    <path d="M77.4 196.2 c-8 -6.9 -11.4 -16.9 -11.4 -33.2 0.1 -20.9 2.4 -30.2 6.7 -27.4 2.9 1.8 4.3 9.4 5.4 28.9 0.9 16.5 1.4 19.8 3.6 25 1.3 3.3 2.5 6.7 2.6 7.5 0.3 2.6 -3.6 2.1 -6.9 -0.8z"/>
    <path d="M2.9 167.3 c-5.1 -6.1 11.2 -24.2 21.9 -24.3 2.6 0 2.7 1.1 0.6 5.5 -1.4 3 -12.6 13.5 -14.3 13.5 -0.4 0 -1.4 1.2 -2.1 2.6 -2 3.6 -4.5 4.8 -6.1 2.7z"/>
    <path d="M87.4 160.5 c-1.9 -1.9 -2.4 -3.4 -2.2 -5.8 0.4 -4.5 3.2 -4.7 7.4 -0.6 3.9 3.8 4.1 4.9 1.6 7.2 -2.5 2.3 -3.9 2.1 -6.8 -0.8z"/>
    <path d="M126.5 158.9 c-6 -3 -10 -7.4 -17 -18.6 -7.6 -12.2 -8.2 -13.8 -5.8 -15.2 2.8 -1.5 7.3 1.8 21.3 15.4 6.9 6.8 13.5 12.6 14.7 12.9 2.7 0.8 3.9 4.2 2.3 6.1 -1.8 2.2 -10.7 1.8 -15.5 -0.6z"/>
    <path d="M36.2 156.7 c-1.5 -1.8 -1.8 -7.8 -0.4 -10.3 1.9 -3.6 3.8 -4.7 6.1 -3.4 1.7 0.9 2.1 2 2.1 6.1 0 2.8 -0.5 5.9 -1 7 -1.2 2.2 -5.2 2.5 -6.8 0.6z"/>
    <path d="M121.5 116 c-0.8 -2.5 1.1 -5.1 3 -4.4 2 0.8 2.7 3.4 1.4 5 -1.7 2 -3.7 1.7 -4.4 -0.6z"/>
    <path d="M4.3 113 c-2.6 -1.1 -2.8 -1.9 -1 -4.4 1.5 -2 7.5 -2.9 9.5 -1.4 1.8 1.5 1.5 4.6 -0.7 5.8 -2.3 1.2 -4.8 1.2 -7.8 0z"/>
    <path d="M32.7 82 c-6.3 -4 -10.3 -9.9 -13.2 -19.4 -2.5 -8.8 -4 -24 -2.7 -29.5 0.9 -4.2 3.4 -6.1 6 -4.5 1.7 1.1 1.9 2 4.6 17.9 1.8 10.8 7.3 22.5 14.1 30.3 2.8 3.1 4.5 5.8 4.1 6.7 -1 2.6 -7.7 1.8 -12.9 -1.5z"/>
    <path d="M133.6 80.4 c-2.1 -2.1 -2 -2.9 0.6 -5.4 2.8 -2.6 6.6 -3.6 8.6 -2.3 2.5 1.5 2.4 2.9 -0.3 6.2 -2.9 3.4 -6.4 4 -8.9 1.5z"/>
    <path d="M93.3 73.3 c-2 -0.8 -1.6 -2.8 2.6 -11.1 7.1 -14.1 10.8 -19.4 22.7 -31.9 9.8 -10.5 12.1 -12.4 14.3 -12.1 1.5 0.2 2.7 1 2.9 2 0.6 3.2 -18 29.9 -29.4 42.1 -6 6.5 -11.1 11.7 -11.4 11.6 -0.3 0 -1.1 -0.3 -1.7 -0.6z"/>
    <path d="M61.7 56.2 c-2.7 -3 -3.3 -7.9 -1.2 -10.2 1 -1.1 2.4 -2 3.1 -2 2.1 0 4.4 4.2 4.4 8 0 5.6 -3.1 7.7 -6.3 4.2z"/>
    <path d="M96.2 18.8 c-4.2 -4.2 4 -19.3 8.8 -16.3 1.8 1.1 1 6.9 -1.7 12.2 -2.6 5.3 -4.7 6.5 -7.1 4.1z"/>
    </g>`

  const div = document.createElement("div")
  div.className = "sidebar-section-link-wrapper"
  div.innerHTML = `
            <a href="https://www.latranchee.com" class="sidebar-section-link sidebar-section-link-everything sidebar-row">
              <span class="sidebar-section-link-prefix icon">
                <svg viewBox="0 0 100 100" class="logoIcon" xmlns="http://www.w3.org/2000/svg">${logo}</svg>
              </span>
              <span class="sidebar-section-link-content-text"> Accueil </span>
            </a>
      `

  const customHeader = document.createElement("div")
  customHeader.className = "sidebar-section-wrapper sidebar-section-community"
  customHeader.innerHTML = `
            <div class="sidebar-section-header-wrapper sidebar-row">
              <button id="ember11" class="sidebar-section-header sidebar-section-header-collapsable btn-flat btn no-text" type="button">
                <span class="sidebar-section-header-text"> Camp d'entraînement </span>
              </button>
          </div>
          <div class="sidebar-section-content" id="customNavigation"/>
      `

  $(document).ready(function () {
    // 링크 생성
    const links = [
      { title: "Accueil", src: "https://www.latranchee.com", svg: logo, viewbox: "0 0 100 100" },
      { title: "Formations", src: "https://www.latranchee.com/formations", svg: mdiSchool, viewbox: "2 -2 16 16" },
      { title: "Blogue", src: "https://www.latranchee.com/blogue", viewbox: "1 -3 16 16", svg: rss },
    ]

    // 모바일
    let hamburger = document.getElementById("toggle-hamburger-menu")
    if (hamburger) {
      hamburger.addEventListener("click", addCustomLinks)
    } else {
      addCustomLinks()
    }
    
    let bool = false;
    function addCustomLinks() {
      setTimeout(function () {
        // 내비게이션이 로드될 때까지 기다리게 강제
        const sidebar = document.getElementsByClassName("sidebar-sections")[0]
        if (sidebar) {
          sidebar.prepend(customHeader)
          if (bool) return;
          // customNav ID 가져오기
          const customNavigation = document.getElementById("customNavigation")
          if (customNavigation) {
            links.filter(function (link) {
              let linkDiv = document.createElement("div")
              linkDiv.className = "sidebar-section-link-wrapper"
              linkDiv.innerHTML = `<a href="${link.src}" class="sidebar-section-link sidebar-section-link-everything sidebar-row ember-view">
                        <span class="sidebar-section-link-prefix icon" id="link_${link.title}"></span>
                        <span class="sidebar-section-link-content-text"> ${link.title} </span>
                    </a>
                  `
              customNavigation.append(linkDiv)
              let linkIcon = document.getElementById("link_" + link.title)
              if (linkIcon && link.svg) {
                linkIcon.innerHTML = `<svg viewBox="${link.viewbox}" class="logoIcon" xmlns="http://www.w3.org/2000/svg"> ${link.svg}</svg>`
              }
            })
          }
        }
        bool = true
      }, 0)
      
    }
  })
</script>

누군가에게 도움이 되길 바랍니다!

2개의 좋아요

저도 동의합니다. 사이드바에 커스텀 링크나 상단에 버튼이 좀 더 추가되는 등 커스터마이징 옵션이 더 많아지면 좋겠어요!

고려해 주셔서 감사합니다. 제 포럼의 사용자 기반에 있어 이 기능은 꽤 중요하다고 생각합니다. 사용자는 보통 약간 숨겨진 정보를 발견하는 데 능숙하지 못하며, "관리자에게 문의"나 "포럼 규칙"과 같은 항목에 눈에 띄는 링크를 추가해야 합니다. 커뮤니티 제목 바로 아래에 직접 배치되는 것은 괜찮지만, 더 보기 메뉴 아래에는 절대 발견되지 않을 것입니다. 또한 내부 및 외부 링크를 사용할 수 있는 유연성도 중요합니다. 현재 커스텀 햄버거 메뉴에서는 외부 링크가 깨져 있습니다:

1개의 좋아요

위 예제들을 바탕으로 누군가에게 도움이 되기를 바라며 또 다른 예시를 하나 더 소개합니다. 이 코드는 완전히 새로운 섹션을 추가하는 것이 아니라, 커뮤니티 섹션의 “더 보기” 패널 하단에 추가 링크를 삽입합니다(푸터의 FAQ 및 소개 링크 앞). FontAwesome 아이콘(사이트 설정에 추가된 경우)과 외부 링크를 지원합니다. 사이드바가 닫혔다가 다시 열리거나, 커뮤니티 섹션이 접혔다가 다시 확장되는 경계 상황도 처리합니다. 데스크톱과 모바일 모두에서 작동합니다.

저는 JavaScript 전문가가 아니므로, 비효율적이거나 최적화되지 않은 코드가 있을 수 있습니다. 하지만 제 사이트에서는 적어도 의도한 대로 작동하는 것 같습니다.

아래 코드를 테마 구성 요소의 Header 탭에 붙여넣고 필요에 맞게 수정하세요:

<script>
const links = [
    // 올바르게 표시되지 않으면 사이트 설정에 FontAwesome 아이콘을 추가해야 할 수 있습니다
    { title: "My Account", src: "/my/billing/subscriptions", icon: "file-invoice-dollar" },
    { title: "User Directory", src: "/u?asc=true&cards=yes&order=username&period=all", icon: "address-book" },
    { title: "Docs", src: "/docs", icon: "book-reader" },
    { title: "External Site", src: "https://google.com/", icon: "globe" }
]

$(document).ready(function () {
    if (document.getElementById("toggle-hamburger-menu")) {
        // 모바일 뷰입니다
        addToggleListener(document.getElementById("toggle-hamburger-menu"))
    } else {
        // 데스크톱 뷰입니다
        addToggleListener(document.getElementsByClassName("btn-sidebar-toggle")[0])
        addHeaderListener()
        addMoreListener()
    }

    function addToggleListener(toggleEl) {
        if (toggleEl) {
            toggleEl.addEventListener("click", function () {
                // 사이드바가 로드될 때까지 잠시 대기
                setTimeout(function() {
                    let sidebar = document.getElementsByClassName("sidebar-section-header").length
                    if (sidebar) {
                        addHeaderListener()
                        addMoreListener()
                    }
                }, 100)
            })
        }
    }

    function addHeaderListener() {
        let communityHeader = document.getElementsByClassName("sidebar-section-header")[0]
        if (communityHeader) {
            communityHeader.addEventListener("click", function () {
                // 섹션이 확장될 때까지 잠시 대기
                setTimeout(function() {
                    let communitySection = document.getElementById("sidebar-section-content-community")
                    if (communitySection) {
                        addMoreListener()
                    }
                }, 100)
            })
        }
    }
    
    function addMoreListener() {
        let buttonMore = document.getElementsByClassName("sidebar-more-section-links-details")[0]
        if (buttonMore) {
            buttonMore.addEventListener("click", addCustomLinks)
        }
    }
    
    function addCustomLinks() {
        // 내비게이션이 로드될 때까지 잠시 대기
        setTimeout(function () {
            const parentEl = document.getElementsByClassName("sidebar-more-section-links-details-content-main")[0]
            let linksAlreadyAdded = document.getElementsByClassName("sidebar-section-custom-link").length
        
            if (parentEl && !linksAlreadyAdded) {
                links.filter(function (link) {
                    let linkDiv = document.createElement("li")
                    let linkTitleTrim = link.title.replace(/\s+/g, '')
                    linkDiv.className = "sidebar-section-link-wrapper sidebar-section-custom-link"
                    linkDiv.innerHTML = `<a href="${link.src}" class="sidebar-section-link sidebar-section-link-everything sidebar-row ember-view">
                            <span class="sidebar-section-link-prefix icon" id="link_${linkTitleTrim}"></span>
                            <span class="sidebar-section-link-content-text"> ${link.title} </span>
                        </a>
                      `
                    parentEl.append(linkDiv)
                    
                    let linkIcon = document.getElementById("link_" + linkTitleTrim)
                    if (linkIcon && link.icon) {
                        linkIcon.innerHTML = `<svg viewBox="0 0 640 512" class="fa d-icon svg-icon prefix-icon svg-string d-icon-${link.icon}" xmlns="http://www.w3.org/2000/svg">
                                <use xlink:href="#${link.icon}"></use>
                            </svg>
                        `
                    }
                })
            }
        }, 100)
    }
})
</script>
5개의 좋아요

@Ryan_Hyer 정말 멋지네요! 햄버거 메뉴 토글 이벤트 후 아이템이 나타나거나 계속 나타나게 하는 방법을 찾아내셨네요. 제가 여기서 겪었던 문제가 바로 그것이었어요:

코드가 매우 깔끔하고 정리 잘 되어 있네요. 덕분에 커뮤니티 메뉴에서 원하던 항목이 “더보기” 아래로 숨겨지지 않도록 표시되게 할 수 있었습니다.

헤더:

<script>

const links = [
    // FontAwesome 아이콘이 제대로 나타나지 않는 경우 사이트 설정에 추가해야 할 수 있습니다.
    { title: "사용자 디렉토리", src: "/u?asc=true&cards=yes&order=username&period=all", icon: "address-book" },
    { title: "문서", src: "/docs", icon: "book-reader" },
    { title: "외부 사이트", src: "https://google.com/", icon: "globe" }
]

$(document).ready(function () {
    if (document.getElementById("toggle-hamburger-menu")) {
        // 모바일 뷰입니다.
        addToggleListener(document.getElementById("toggle-hamburger-menu"))
    } else {
        // 데스크톱 뷰입니다.
        addToggleListener(document.getElementsByClassName("btn-sidebar-toggle")[0])
        addCustomLinks()
    }

    function addToggleListener(toggleEl) {
        if (toggleEl) {
            toggleEl.addEventListener("click", function () {
                // 사이드바가 로드될 때까지 잠시 대기합니다.
                setTimeout(function() {
                    let sidebar = document.getElementsByClassName("sidebar-section-header").length
                    if (sidebar) {
                        addCustomLinks()
                    }
                }, 100)
            })
        }
    }
    
    function addCustomLinks() {
        // 네비게이션이 로드될 때까지 잠시 대기합니다.
        setTimeout(function () {
            const parentEl = document.getElementsByClassName("sidebar-section-content")[0]
            let linksAlreadyAdded = document.getElementsByClassName("sidebar-section-custom-link").length
        
            if (parentEl && !linksAlreadyAdded) {
                links.filter(function (link) {
                    let linkDiv = document.createElement("li")
                    let linkTitleTrim = link.title.replace(/\s+/g, '')
                    linkDiv.className = "sidebar-section-link-wrapper sidebar-section-custom-link"
                    linkDiv.innerHTML = `<a href="${link.src}" class="sidebar-section-link sidebar-section-link-everything sidebar-row ember-view">
                            <span class="sidebar-section-link-prefix icon" id="link_${linkTitleTrim}"></span>
                            <span class="sidebar-section-link-content-text"> ${link.title} </span>
                        </a>
                      `
                    parentEl.append(linkDiv)
                    
                    let linkIcon = document.getElementById("link_" + linkTitleTrim)
                    if (linkIcon && link.icon) {
                        linkIcon.innerHTML = `<svg viewBox="0 0 640 512" class="fa d-icon svg-icon prefix-icon svg-string d-icon-${link.icon}" xmlns="http://www.w3.org/2000/svg">
                                <use xlink:href="#${link.icon}"></use>
                            </svg>
                        `
                    }
                })
            }
        }, 100)
    }
})
</script>

그리고 보완할 CSS:

.sidebar-section-content {
  display: flex; /* 순서를 재배열할 수 있도록 flex 레이아웃 설정 */
  flex-direction: column;
  .sidebar-more-section-links-details {
    order: +1;
  }
}

.sidebar-wrapper li a.sidebar-section-link-about {
    display: none;
}

.sidebar-wrapper li a.sidebar-section-link-faq {
    display: none;
}

.sidebar-more-section-links-details-content-secondary .sidebar-section-link.sidebar-section-link-about {
    display: none;
}

.sidebar-more-section-links-details-content-secondary .sidebar-section-link.sidebar-section-link-faq {
    display: none;
}
2개의 좋아요

방금 올라온 이 주제에 대해 주목해 주셨으면 합니다:

해당 주제에서 피드백을 남겨 주세요!

6개의 좋아요

이 주제는 42시간 후 자동으로 닫혔습니다. 더 이상 새로운 답변을 작성할 수 없습니다.