Nft 스마트 컨트랙트를 만들어 배포했봤는데 문제가 있습니다

보통 NFT 컨트랙트는 Klaytnscope에서 NFT라고 표기가 되는 것 같은데 제 컨트랙트는 그렇지 않네요.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "./NFTCore.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";

contract NFTSampleis is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
   
   address private _thisAddr;
   NFTCore private _nftCore;
   uint256 private _tokenId = 0;

   constructor(string memory _name, string memory _symbol, address _nftCoreAddress) {
        _thisAddr = address(this);
        _nftCore = NFTCore(_nftCoreAddress);
        _nftCore.init(_thisAddr, _name, _symbol);
   }

    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    function balanceOf(address owner) external view returns (uint256 balance) {
        return _nftCore.balanceOf(_thisAddr, owner);
    }

    function ownerOf(uint256 tokenId) external view returns (address owner) {
        return _nftCore.ownerOf(_thisAddr, tokenId);
    }

    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external {
        _nftCore.safeTransferFrom(_thisAddr, from, to, tokenId, data);   
    }

    function safeTransferFrom(address from, address to, uint256 tokenId) external {
        _nftCore.safeTransferFrom(_thisAddr, from, to, tokenId);  
    }

    function transferFrom(address from, address to, uint256 tokenId) external {
        _nftCore.transferFrom(_thisAddr, from, to, tokenId);
    }

    function approve(address to, uint256 tokenId) external {
        _nftCore.approve(_thisAddr, to, tokenId);
    }

    function setApprovalForAll(address operator, bool approved) external {
        _nftCore.setApprovalForAll(_thisAddr, operator, approved);
    }

    function getApproved(uint256 tokenId) external view returns (address operator) {
        return _nftCore.getApproved(_thisAddr, tokenId);
    }

    function isApprovedForAll(address owner, address operator) external view returns (bool) {
        return _nftCore.isApprovedForAll(_thisAddr, owner, operator);
    }

    function mint(address to) public {
        _nftCore._mint(_thisAddr, to, _tokenId++);
    }

    function name() external view returns (string memory) {
        return _nftCore.name(_thisAddr);
    }

    function symbol() external view returns (string memory) {
        return _nftCore.symbol(_thisAddr);
    }

     function tokenURI(uint256 tokenId) external view returns (string memory) {
        return _nftCore.tokenURI(_thisAddr,tokenId);
     }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract  NFTCore is Context, IERC721Errors, ERC165 {
//contract NFTCore is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
    using Strings for uint256;

    mapping (address => string) __name;
    mapping (address => string) __symbol;
    mapping (address => mapping(uint256 tokenId => address)) __owners;
    mapping (address => mapping(address owner => uint256)) __balances;
    mapping (address => mapping(uint256 tokenId => address)) __tokenApprovals;
    mapping (address =>  mapping(address owner => mapping(address operator => bool))) __operatorApprovals;


    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    function init(address contractAddr, string memory _name, string memory _symbol) public{
        __name[contractAddr] = _name;
        __symbol[contractAddr] = _symbol;
    }

    function balanceOf(address contractAddr, address owner) public view returns (uint256) {
        if (owner == address(0)) {
            revert ERC721InvalidOwner(address(0));
        }
        return __balances[contractAddr][owner];
    }

    function ownerOf(address contractAddr, uint256 tokenId) public view returns (address) {
        return _requireOwned(contractAddr, tokenId);
    }

    function name(address contractAddr) public view returns (string memory) {
        return __name[contractAddr];
    }


    function symbol(address contractAddr) public view returns (string memory) {
        return __symbol[contractAddr];
    }

    function tokenURI(address contractAddr, uint256 tokenId) public view virtual returns (string memory) {
        _requireOwned(contractAddr, tokenId);

        string memory baseURI = _baseURI(contractAddr);
        return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
    }

    function _baseURI(address contractAddr) internal view virtual returns (string memory) {
        return "";
    }


    function getApproved(address contractAddr, uint256 tokenId) public view virtual returns (address) {
        _requireOwned(contractAddr, tokenId);

        return _getApproved(contractAddr, tokenId);
    }

    function approve(address contractAddr, address to, uint256 tokenId) public virtual {
        _approve(contractAddr, to, tokenId, _msgSender());
    }

    function _approve(address contractAddr, address to, uint256 tokenId, address auth) internal {
        _approve(contractAddr, to, tokenId, auth, true);
    }

    function _approve(address contractAddr, address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
        // Avoid reading the owner unless necessary
        if (emitEvent || auth != address(0)) {
            address owner = _requireOwned(contractAddr, tokenId);

            // We do not use _isAuthorized because single-token approvals should not be able to call approve
            if (auth != address(0) && owner != auth && !isApprovedForAll(contractAddr, owner, auth)) {
                revert ERC721InvalidApprover(auth);
            }

            if (emitEvent) {
                emit Approval(owner, to, tokenId);
            }
        }

        __tokenApprovals[contractAddr][tokenId] = to;
    }

    function isApprovedForAll(address contractAddr, address owner, address operator) public view virtual returns (bool) {
        return __operatorApprovals[contractAddr][owner][operator];
    }

    function _requireOwned(address contractAddr, uint256 tokenId) internal view returns (address) {
        address owner = _ownerOf(contractAddr, tokenId);
        if (owner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        }
        return owner;
    }

    function _ownerOf(address contractAddr, uint256 tokenId) internal view virtual returns (address) {
        return __owners[contractAddr][tokenId];
    }

     function transferFrom(address contractAddr, address from, address to, uint256 tokenId) public virtual {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        // Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
        // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
        address previousOwner = _update(contractAddr, to, tokenId, _msgSender());
        if (previousOwner != from) {
            revert ERC721IncorrectOwner(from, tokenId, previousOwner);
        }
    }


    function safeTransferFrom(address contractAddr, address from, address to, uint256 tokenId) public {
        safeTransferFrom(contractAddr, from, to, tokenId, "");
    }

    function safeTransferFrom(address contractAddr, address from, address to, uint256 tokenId, bytes memory data) public virtual {
        transferFrom(contractAddr, from, to, tokenId);
        _checkOnERC721Received(contractAddr, from, to, tokenId, data);
    }

    function _update(address contractAddr, address to, uint256 tokenId, address auth) internal virtual returns (address) {
        address from = _ownerOf(contractAddr, tokenId);

        // Perform (optional) operator check
        if (auth != address(0)) {
            _checkAuthorized(contractAddr, from, auth, tokenId);
        }

        // Execute the update
        if (from != address(0)) {
            // Clear approval. No need to re-authorize or emit the Approval event
            _approve(contractAddr, address(0), tokenId, address(0), false);

            unchecked {
                __balances[contractAddr][from] -= 1;
            }
        }

        if (to != address(0)) {
            unchecked {
                __balances[contractAddr][to] += 1;
            }
        }

        __owners[contractAddr][tokenId] = to;

        emit Transfer(from, to, tokenId);

        return from;
    }

       function _checkAuthorized(address contractAddr, address owner, address spender, uint256 tokenId) internal view virtual {
        if (!_isAuthorized(contractAddr, owner, spender, tokenId)) {
            if (owner == address(0)) {
                revert ERC721NonexistentToken(tokenId);
            } else {
                revert ERC721InsufficientApproval(spender, tokenId);
            }
        }
    }

    function _isAuthorized(address contractAddr, address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
        return
            spender != address(0) &&
            (owner == spender || isApprovedForAll(contractAddr, owner, spender) || _getApproved(contractAddr, tokenId) == spender);
    }

    function _getApproved(address contractAddr, uint256 tokenId) internal view virtual returns (address) {
        return __tokenApprovals[contractAddr][tokenId];
    }

   function setApprovalForAll(address contractAddr,address operator, bool approved) public virtual {
        _setApprovalForAll(contractAddr, _msgSender(), operator, approved);
    }

    function _setApprovalForAll(address contractAddr, address owner, address operator, bool approved) internal virtual {
        if (operator == address(0)) {
            revert ERC721InvalidOperator(operator);
        }
        __operatorApprovals[contractAddr][owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }


    function _increaseBalance(address contractAddr, address account, uint128 value) internal virtual {
        unchecked {
            __balances[contractAddr][account] += value;
        }
    }

    function _mint(address contractAddr,address to, uint256 tokenId) public {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        address previousOwner = _update(contractAddr, to, tokenId, address(0));
        if (previousOwner != address(0)) {
            revert ERC721InvalidSender(address(0));
        }
    }

    function _safeMint(address contractAddr,address to, uint256 tokenId) internal {
        _safeMint(contractAddr, to, tokenId, "");
    }

    function _safeMint(address contractAddr, address to, uint256 tokenId, bytes memory data) internal virtual {
        _mint(contractAddr, to, tokenId);
        _checkOnERC721Received(contractAddr, address(0), to, tokenId, data);
    }

function _transfer(address contractAddr, address from, address to, uint256 tokenId) internal {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        address previousOwner = _update(contractAddr, to, tokenId, address(0));
        if (previousOwner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        } else if (previousOwner != from) {
            revert ERC721IncorrectOwner(from, tokenId, previousOwner);
        }
    }

    function _safeTransfer(address contractAddr, address from, address to, uint256 tokenId) internal {
        _safeTransfer(contractAddr, from, to, tokenId, "");
    }

    function _safeTransfer(address contractAddr, address from, address to, uint256 tokenId, bytes memory data) internal virtual {
        _transfer(contractAddr, from, to, tokenId);
        _checkOnERC721Received(contractAddr, from, to, tokenId, data);
    }

    function _checkOnERC721Received(address contractAddr, address from, address to, uint256 tokenId, bytes memory data) private {
        if (to.code.length > 0) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                if (retval != IERC721Receiver.onERC721Received.selector) {
                    revert ERC721InvalidReceiver(to);
                }
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert ERC721InvalidReceiver(to);
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        }
    }

    function _burn(address contractAddr, uint256 tokenId) internal {
        address previousOwner = _update(contractAddr, address(0), tokenId, address(0));
        if (previousOwner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        }
    }

}

저는 NFTCore와 NFTSample이라는 컨트랙트를 만들었습니다.

NFTCore 컨트랙트는 실제 ERC721 인터페이스 구현체 입니다.
NFTSample는 NFTCore의 주소를 받아 NFT 기능은 NFTCore에 의해서 작동됩니다.

제 생각엔 ERC721을 준수하기 때문에 정상적으로 오픈시에서 NFT가 보여야 할 것 같은데 그렇지 않네요.

제 컨트랙트에 문제가 있는 걸까요?

안녕하세요, 말씀하신대로 ERC721을 구현했다면 일반적으로 NFT로 표현될 가능성이 높지만
클레이튼스코프 및 클레이튼파인더에서도 NFT로 표기하는것은 현재 표준이 존재하는 것은 아니기때문에 서비스 운영사에 따라 차이가 있습니다.

따라서 오픈씨에서 NFT로 표현되기 위한 방법은 오픈씨측에 문의해 보시는게 가장 정확합니다.